diff options
942 files changed, 33430 insertions, 9964 deletions
diff --git a/.clang-format b/.clang-format index 7b8e0ef8eba..7e88e6d1cb1 100644 --- a/.clang-format +++ b/.clang-format @@ -266,6 +266,7 @@ ForEachMacros: - MAP_SLOT_PROBING_BEGIN - VECTOR_SET_SLOT_PROBING_BEGIN - WL_ARRAY_FOR_EACH + - FOREACH_SPECTRUM_CHANNEL StatementMacros: - PyObject_HEAD diff --git a/CMakeLists.txt b/CMakeLists.txt index c998919622e..9ac3e379f5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,7 @@ endif() option(WITH_GMP "Enable features depending on GMP (Exact Boolean)" ON) # Compositor -option(WITH_COMPOSITOR "Enable the tile based nodal compositor" ON) +option(WITH_COMPOSITOR_CPU "Enable the tile based CPU nodal compositor" ON) option(WITH_OPENIMAGEDENOISE "Enable the OpenImageDenoise compositing node" ON) option(WITH_OPENSUBDIV "Enable OpenSubdiv for surface subdivision" ON) @@ -223,7 +223,7 @@ if(UNIX AND NOT (APPLE OR HAIKU)) option(WITH_GHOST_WAYLAND "Enable building Blender against Wayland for windowing (under development)" OFF) mark_as_advanced(WITH_GHOST_WAYLAND) - if (WITH_GHOST_WAYLAND) + if(WITH_GHOST_WAYLAND) option(WITH_GHOST_WAYLAND_LIBDECOR "Optionally build with LibDecor window decorations" OFF) mark_as_advanced(WITH_GHOST_WAYLAND_LIBDECOR) @@ -366,7 +366,7 @@ if(WIN32 OR APPLE) endif() option(WITH_INPUT_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ON) if(UNIX AND NOT APPLE) - option(WITH_INSTALL_PORTABLE "Install redistributeable runtime, otherwise install into CMAKE_INSTALL_PREFIX" ON) + option(WITH_INSTALL_PORTABLE "Install redistributable runtime, otherwise install into CMAKE_INSTALL_PREFIX" ON) option(WITH_STATIC_LIBS "Try to link with static libraries, as much as possible, to make blender more portable across distributions" OFF) if(WITH_STATIC_LIBS) option(WITH_BOOST_ICU "Boost uses ICU library (required for linking with static Boost built with libicu)." OFF) @@ -464,8 +464,8 @@ if(NOT APPLE) option(WITH_CYCLES_ONEAPI_SYCL_HOST_ENABLED "Enable use of SYCL host (CPU) device execution by oneAPI implementation. This option is for debugging purposes and impacts GPU execution." OFF) # https://www.intel.com/content/www/us/en/develop/documentation/oneapi-dpcpp-cpp-compiler-dev-guide-and-reference/top/compilation/ahead-of-time-compilation.html - SET (CYCLES_ONEAPI_SPIR64_GEN_DEVICES "dg2" CACHE STRING "oneAPI Intel GPU architectures to build binaries for") - SET (CYCLES_ONEAPI_SYCL_TARGETS spir64 spir64_gen CACHE STRING "oneAPI targets to build AOT binaries for") + set(CYCLES_ONEAPI_SPIR64_GEN_DEVICES "dg2" CACHE STRING "oneAPI Intel GPU architectures to build binaries for") + set(CYCLES_ONEAPI_SYCL_TARGETS spir64 spir64_gen CACHE STRING "oneAPI targets to build AOT binaries for") mark_as_advanced(WITH_CYCLES_ONEAPI_SYCL_HOST_ENABLED) mark_as_advanced(CYCLES_ONEAPI_SPIR64_GEN_DEVICES) @@ -580,14 +580,14 @@ endif() # Metal -if (APPLE) +if(APPLE) option(WITH_METAL_BACKEND "Use Metal for graphics instead of (or as well as) OpenGL on macOS." OFF) mark_as_advanced(WITH_METAL_BACKEND) else() set(WITH_METAL_BACKEND OFF) endif() -if (WITH_METAL_BACKEND) +if(WITH_METAL_BACKEND) set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version" FORCE) endif() @@ -1196,7 +1196,8 @@ if(WITH_OPENVDB) list(APPEND OPENVDB_INCLUDE_DIRS ${BOOST_INCLUDE_DIR} ${TBB_INCLUDE_DIRS} - ${OPENEXR_INCLUDE_DIRS}) + ${OPENEXR_INCLUDE_DIRS} + ) list(APPEND OPENVDB_LIBRARIES ${OPENEXR_LIBRARIES} ${ZLIB_LIBRARIES}) @@ -1346,7 +1347,7 @@ endif() #----------------------------------------------------------------------------- # Configure Metal. -if (WITH_METAL_BACKEND) +if(WITH_METAL_BACKEND) add_definitions(-DWITH_METAL_BACKEND) # No need to add frameworks here, all the ones we need for Metal and diff --git a/build_files/build_environment/cmake/download.cmake b/build_files/build_environment/cmake/download.cmake index 76744aa9b0c..547bf77f8dd 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.3) +# set(BLENDER_VERSION 3.1) function(download_source dep) set(TARGET_FILE ${${dep}_FILE}) diff --git a/build_files/build_environment/cmake/harvest.cmake b/build_files/build_environment/cmake/harvest.cmake index bc456858d2a..7ebad7a3da2 100644 --- a/build_files/build_environment/cmake/harvest.cmake +++ b/build_files/build_environment/cmake/harvest.cmake @@ -43,7 +43,8 @@ function(harvest from to) install( FILES ${LIBDIR}/${from} DESTINATION ${HARVEST_TARGET}/${dirpath} - RENAME ${filename}) + RENAME ${filename} + ) else() install( DIRECTORY ${LIBDIR}/${from}/ @@ -53,7 +54,8 @@ function(harvest from to) PATTERN "pkgconfig" EXCLUDE PATTERN "cmake" EXCLUDE PATTERN "__pycache__" EXCLUDE - PATTERN "tests" EXCLUDE) + PATTERN "tests" EXCLUDE + ) endif() endfunction() diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake index e09577ac802..27577a9fbf7 100644 --- a/build_files/cmake/config/blender_full.cmake +++ b/build_files/cmake/config/blender_full.cmake @@ -13,7 +13,7 @@ set(WITH_BULLET ON CACHE BOOL "" FORCE) set(WITH_CODEC_AVI ON CACHE BOOL "" FORCE) set(WITH_CODEC_FFMPEG ON CACHE BOOL "" FORCE) set(WITH_CODEC_SNDFILE ON CACHE BOOL "" FORCE) -set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE) +set(WITH_COMPOSITOR_CPU ON CACHE BOOL "" FORCE) set(WITH_CYCLES ON CACHE BOOL "" FORCE) set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE) set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 5ce344d39e8..c98dfe27285 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -18,7 +18,7 @@ set(WITH_BULLET OFF CACHE BOOL "" FORCE) set(WITH_CODEC_AVI OFF CACHE BOOL "" FORCE) set(WITH_CODEC_FFMPEG OFF CACHE BOOL "" FORCE) set(WITH_CODEC_SNDFILE OFF CACHE BOOL "" FORCE) -set(WITH_COMPOSITOR OFF CACHE BOOL "" FORCE) +set(WITH_COMPOSITOR_CPU OFF CACHE BOOL "" FORCE) set(WITH_COREAUDIO OFF CACHE BOOL "" FORCE) set(WITH_CYCLES OFF CACHE BOOL "" FORCE) set(WITH_DRACO OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index 2567e0b444a..3661b1de805 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -14,7 +14,7 @@ set(WITH_BULLET ON CACHE BOOL "" FORCE) set(WITH_CODEC_AVI ON CACHE BOOL "" FORCE) set(WITH_CODEC_FFMPEG ON CACHE BOOL "" FORCE) set(WITH_CODEC_SNDFILE ON CACHE BOOL "" FORCE) -set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE) +set(WITH_COMPOSITOR_CPU ON CACHE BOOL "" FORCE) set(WITH_CYCLES ON CACHE BOOL "" FORCE) set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE) set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index f6e233a0c86..0d7ed9d618c 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -96,6 +96,18 @@ find_package_wrapper(PNG REQUIRED) find_package_wrapper(ZLIB REQUIRED) find_package_wrapper(Zstd REQUIRED) +function(check_freetype_for_brotli) + include(CheckSymbolExists) + set(CMAKE_REQUIRED_INCLUDES ${FREETYPE_INCLUDE_DIRS}) + check_symbol_exists(FT_CONFIG_OPTION_USE_BROTLI + "freetype/config/ftconfig.h" HAVE_BROTLI) + if(NOT HAVE_BROTLI) + unset(HAVE_BROTLI CACHE) + message(FATAL_ERROR "Freetype needs to be compiled with brotli support!") + endif() + unset(HAVE_BROTLI CACHE) +endfunction() + if(NOT WITH_SYSTEM_FREETYPE) # FreeType compiled with Brotli compression for woff2. find_package_wrapper(Freetype REQUIRED) @@ -110,6 +122,7 @@ if(NOT WITH_SYSTEM_FREETYPE) # ${BROTLI_LIBRARIES} # ) endif() + check_freetype_for_brotli() endif() if(WITH_PYTHON) @@ -587,6 +600,7 @@ if(WITH_SYSTEM_FREETYPE) if(NOT FREETYPE_FOUND) message(FATAL_ERROR "Failed finding system FreeType version!") endif() + check_freetype_for_brotli() endif() if(WITH_LZO AND WITH_SYSTEM_LZO) @@ -789,7 +803,8 @@ if(CMAKE_COMPILER_IS_GNUCC) "The mold linker could not find the directory containing the linker command " "(typically " "\"${MOLD_PREFIX}/libexec/mold/ld\") or " - "\"${MOLD_PREFIX}/lib/mold/ld\") using system linker.") + "\"${MOLD_PREFIX}/lib/mold/ld\") using system linker." + ) set(WITH_LINKER_MOLD OFF) endif() unset(MOLD_PREFIX) @@ -928,7 +943,8 @@ function(CONFIGURE_ATOMIC_LIB_IF_NEEDED) int main(int argc, char **argv) { std::atomic<uint64_t> uint64; uint64++; return 0; - }") + }" + ) include(CheckCXXSourceCompiles) check_cxx_source_compiles("${_source}" ATOMIC_OPS_WITHOUT_LIBATOMIC) diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index 7e272ea26b0..e81582f0460 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -146,7 +146,7 @@ endif() if(WITH_COMPILER_ASAN AND MSVC AND NOT MSVC_CLANG) if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 19.28.29828) #set a flag so we don't have to do this comparison all the time - SET(MSVC_ASAN ON) + set(MSVC_ASAN ON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fsanitize=address") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /fsanitize=address") string(APPEND CMAKE_EXE_LINKER_FLAGS_DEBUG " /INCREMENTAL:NO") @@ -185,20 +185,20 @@ endif() if(WITH_WINDOWS_SCCACHE) - set(CMAKE_C_COMPILER_LAUNCHER sccache) - set(CMAKE_CXX_COMPILER_LAUNCHER sccache) + set(CMAKE_C_COMPILER_LAUNCHER sccache) + set(CMAKE_CXX_COMPILER_LAUNCHER sccache) + set(SYMBOL_FORMAT /Z7) + set(SYMBOL_FORMAT_RELEASE /Z7) +else() + unset(CMAKE_C_COMPILER_LAUNCHER) + unset(CMAKE_CXX_COMPILER_LAUNCHER) + if(MSVC_ASAN) set(SYMBOL_FORMAT /Z7) set(SYMBOL_FORMAT_RELEASE /Z7) -else() - unset(CMAKE_C_COMPILER_LAUNCHER) - unset(CMAKE_CXX_COMPILER_LAUNCHER) - if(MSVC_ASAN) - set(SYMBOL_FORMAT /Z7) - set(SYMBOL_FORMAT_RELEASE /Z7) - else() - set(SYMBOL_FORMAT /ZI) - set(SYMBOL_FORMAT_RELEASE /Zi) - endif() + else() + set(SYMBOL_FORMAT /ZI) + set(SYMBOL_FORMAT_RELEASE /Zi) + endif() endif() if(WITH_WINDOWS_PDB) @@ -416,7 +416,7 @@ if(WITH_CODEC_FFMPEG) ${LIBDIR}/ffmpeg/lib/avdevice.lib ${LIBDIR}/ffmpeg/lib/avutil.lib ${LIBDIR}/ffmpeg/lib/swscale.lib - ) + ) endif() endif() @@ -565,12 +565,14 @@ if(WITH_BOOST) if(WITH_CYCLES AND WITH_CYCLES_OSL) set(BOOST_LIBRARIES ${BOOST_LIBRARIES} optimized ${BOOST_LIBPATH}/libboost_wave-${BOOST_POSTFIX} - debug ${BOOST_LIBPATH}/libboost_wave-${BOOST_DEBUG_POSTFIX}) + debug ${BOOST_LIBPATH}/libboost_wave-${BOOST_DEBUG_POSTFIX} + ) endif() if(WITH_INTERNATIONAL) set(BOOST_LIBRARIES ${BOOST_LIBRARIES} optimized ${BOOST_LIBPATH}/libboost_locale-${BOOST_POSTFIX} - debug ${BOOST_LIBPATH}/libboost_locale-${BOOST_DEBUG_POSTFIX}) + debug ${BOOST_LIBPATH}/libboost_locale-${BOOST_DEBUG_POSTFIX} + ) endif() else() # we found boost using find_package set(BOOST_INCLUDE_DIR ${Boost_INCLUDE_DIRS}) @@ -677,7 +679,8 @@ if(WITH_OPENIMAGEDENOISE) optimized ${OPENIMAGEDENOISE_LIBPATH}/dnnl.lib debug ${OPENIMAGEDENOISE_LIBPATH}/OpenImageDenoise_d.lib debug ${OPENIMAGEDENOISE_LIBPATH}/common_d.lib - debug ${OPENIMAGEDENOISE_LIBPATH}/dnnl_d.lib) + debug ${OPENIMAGEDENOISE_LIBPATH}/dnnl_d.lib + ) set(OPENIMAGEDENOISE_DEFINITIONS) endif() @@ -832,7 +835,8 @@ if(WITH_CYCLES AND WITH_CYCLES_EMBREE) debug ${LIBDIR}/embree/lib/math_d.lib debug ${LIBDIR}/embree/lib/simd_d.lib debug ${LIBDIR}/embree/lib/sys_d.lib - debug ${LIBDIR}/embree/lib/tasking_d.lib) + debug ${LIBDIR}/embree/lib/tasking_d.lib + ) endif() endif() diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index aedaf35629b..82cd009ea95 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.3-release + - branch: master commit_id: HEAD path: release/scripts/addons - - branch: blender-v3.3-release + - branch: master commit_id: HEAD path: release/scripts/addons_contrib - - branch: blender-v3.3-release + - branch: master commit_id: HEAD path: release/datafiles/locale - - branch: blender-v3.3-release + - branch: master commit_id: HEAD path: source/tools svn: libraries: darwin-arm64: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/darwin_arm64 darwin-x86_64: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/darwin linux-x86_64: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/linux_centos7_x86_64 windows-amd64: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/win64_vc15 tests: - branch: tags/blender-3.3-release + branch: trunk commit_id: HEAD path: lib/tests benchmarks: diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py index bf140812ebb..254cccda301 100755 --- a/build_files/utils/make_update.py +++ b/build_files/utils/make_update.py @@ -110,6 +110,9 @@ def svn_update(args, release_version): if not make_utils.command_missing(args.svn_command): call(svn_non_interactive + ["cleanup", lib_dirpath]) continue + elif dirname.startswith("."): + # Temporary paths such as ".mypy_cache" will report a warning, skip hidden directories. + continue svn_dirpath = os.path.join(dirpath, ".svn") svn_root_dirpath = os.path.join(lib_dirpath, ".svn") diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 0176a888377..18c16a60993 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.3 +PROJECT_NUMBER = V3.4 # 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/extern/CMakeLists.txt b/extern/CMakeLists.txt index 35da67d05a4..4da9631d395 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -48,7 +48,7 @@ if(WITH_LZMA) add_subdirectory(lzma) endif() -if(WITH_CYCLES OR WITH_COMPOSITOR OR WITH_OPENSUBDIV) +if(WITH_CYCLES OR WITH_COMPOSITOR_CPU OR WITH_OPENSUBDIV) add_subdirectory(clew) if((WITH_CYCLES_DEVICE_CUDA OR WITH_CYCLES_DEVICE_OPTIX) AND WITH_CUDA_DYNLOAD) add_subdirectory(cuew) @@ -96,6 +96,6 @@ if(WITH_MOD_FLUID) add_subdirectory(mantaflow) endif() -if(WITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) add_subdirectory(smaa_areatex) endif() diff --git a/intern/cycles/blender/python.cpp b/intern/cycles/blender/python.cpp index 8b2b331f73e..1e33b0b7207 100644 --- a/intern/cycles/blender/python.cpp +++ b/intern/cycles/blender/python.cpp @@ -59,8 +59,6 @@ static void debug_flags_sync_from_scene(BL::Scene b_scene) { DebugFlagsRef flags = DebugFlags(); PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); - /* Synchronize shared flags. */ - flags.viewport_static_bvh = get_enum(cscene, "debug_bvh_type"); /* Synchronize CPU flags. */ flags.cpu.avx2 = get_boolean(cscene, "debug_use_cpu_avx2"); flags.cpu.avx = get_boolean(cscene, "debug_use_cpu_avx"); @@ -140,8 +138,6 @@ static PyObject *init_func(PyObject * /*self*/, PyObject *args) BlenderSession::headless = headless; - DebugFlags().running_inside_blender = true; - Py_RETURN_NONE; } diff --git a/intern/cycles/blender/session.cpp b/intern/cycles/blender/session.cpp index 6d27b8e7d87..e1da85b84ff 100644 --- a/intern/cycles/blender/session.cpp +++ b/intern/cycles/blender/session.cpp @@ -110,7 +110,8 @@ void BlenderSession::create_session() { const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params( + b_scene, background, use_developer_ui); const bool session_pause = BlenderSync::get_session_pause(b_scene, background); /* reset status/progress */ @@ -196,7 +197,8 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params( + b_scene, background, use_developer_ui); if (scene->params.modified(scene_params) || session->params.modified(session_params) || !this->b_render.use_persistent_data()) { @@ -724,7 +726,8 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) /* on session/scene parameter changes, we recreate session entirely */ const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params( + b_scene, background, use_developer_ui); const bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session->params.modified(session_params) || scene->params.modified(scene_params)) { diff --git a/intern/cycles/blender/sync.cpp b/intern/cycles/blender/sync.cpp index 429a8e665af..0d4c1d70180 100644 --- a/intern/cycles/blender/sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -801,7 +801,9 @@ void BlenderSync::free_data_after_sync(BL::Depsgraph &b_depsgraph) /* Scene Parameters */ -SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background) +SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, + const bool background, + const bool use_developer_ui) { SceneParams params; PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); @@ -812,7 +814,7 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background) else if (shadingsystem == 1) params.shadingsystem = SHADINGSYSTEM_OSL; - if (background || DebugFlags().viewport_static_bvh) + if (background || (use_developer_ui && get_enum(cscene, "debug_bvh_type"))) params.bvh_type = BVH_TYPE_STATIC; else params.bvh_type = BVH_TYPE_DYNAMIC; diff --git a/intern/cycles/blender/sync.h b/intern/cycles/blender/sync.h index 0ad4ca6fe83..ae6c2420e55 100644 --- a/intern/cycles/blender/sync.h +++ b/intern/cycles/blender/sync.h @@ -84,7 +84,9 @@ class BlenderSync { } /* get parameters */ - static SceneParams get_scene_params(BL::Scene &b_scene, bool background); + static SceneParams get_scene_params(BL::Scene &b_scene, + const bool background, + const bool use_developer_ui); static SessionParams get_session_params(BL::RenderEngine &b_engine, BL::Preferences &b_userpref, BL::Scene &b_scene, diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index b00515eb037..fbc30234dac 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -343,6 +343,7 @@ set(SRC_UTIL_HEADERS ../util/types_int3_impl.h ../util/types_int4.h ../util/types_int4_impl.h + ../util/types_spectrum.h ../util/types_uchar2.h ../util/types_uchar2_impl.h ../util/types_uchar3.h @@ -356,8 +357,6 @@ set(SRC_UTIL_HEADERS ../util/types_uint4.h ../util/types_uint4_impl.h ../util/types_ushort4.h - ../util/types_vector3.h - ../util/types_vector3_impl.h ) set(LIB diff --git a/intern/cycles/kernel/bake/bake.h b/intern/cycles/kernel/bake/bake.h index ec87990b699..9d53d71b431 100644 --- a/intern/cycles/kernel/bake/bake.h +++ b/intern/cycles/kernel/bake/bake.h @@ -8,6 +8,8 @@ #include "kernel/geom/geom.h" +#include "kernel/util/color.h" + CCL_NAMESPACE_BEGIN ccl_device void kernel_displace_evaluate(KernelGlobals kg, @@ -65,7 +67,7 @@ ccl_device void kernel_background_evaluate(KernelGlobals kg, shader_eval_surface<KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT & ~(KERNEL_FEATURE_NODE_RAYTRACE | KERNEL_FEATURE_NODE_LIGHT_PATH)>( kg, INTEGRATOR_STATE_NULL, &sd, NULL, path_flag); - float3 color = shader_background_eval(&sd); + Spectrum color = shader_background_eval(&sd); #ifdef __KERNEL_DEBUG_NAN__ if (!isfinite_safe(color)) { @@ -76,10 +78,12 @@ ccl_device void kernel_background_evaluate(KernelGlobals kg, /* Ensure finite color, avoiding possible numerical instabilities in the path tracing kernels. */ color = ensure_finite(color); + float3 color_rgb = spectrum_to_rgb(color); + /* Write output. */ - output[offset * 3 + 0] += color.x; - output[offset * 3 + 1] += color.y; - output[offset * 3 + 2] += color.z; + output[offset * 3 + 0] += color_rgb.x; + output[offset * 3 + 1] += color_rgb.y; + output[offset * 3 + 2] += color_rgb.z; } ccl_device void kernel_curve_shadow_transparency_evaluate( diff --git a/intern/cycles/kernel/closure/alloc.h b/intern/cycles/kernel/closure/alloc.h index 933c07a5102..9847898ee89 100644 --- a/intern/cycles/kernel/closure/alloc.h +++ b/intern/cycles/kernel/closure/alloc.h @@ -8,7 +8,7 @@ CCL_NAMESPACE_BEGIN ccl_device ccl_private ShaderClosure *closure_alloc(ccl_private ShaderData *sd, int size, ClosureType type, - float3 weight) + Spectrum weight) { kernel_assert(size <= sizeof(ShaderClosure)); @@ -49,7 +49,7 @@ ccl_device ccl_private void *closure_alloc_extra(ccl_private ShaderData *sd, int ccl_device_inline ccl_private ShaderClosure *bsdf_alloc(ccl_private ShaderData *sd, int size, - float3 weight) + Spectrum weight) { kernel_assert(isfinite_safe(weight)); @@ -74,7 +74,7 @@ ccl_device_inline ccl_private ShaderClosure *bsdf_alloc(ccl_private ShaderData * #ifdef __OSL__ ccl_device_inline ShaderClosure *bsdf_alloc_osl(ShaderData *sd, int size, - float3 weight, + Spectrum weight, void *data) { kernel_assert(isfinite_safe(weight)); diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index 4feb21c43e3..d6b7e7bfa88 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -103,9 +103,8 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private differential3 *domega_in, ccl_private float *pdf) { /* For curves use the smooth normal, particularly for ribbons the geometric @@ -115,304 +114,80 @@ ccl_device_inline int bsdf_sample(KernelGlobals kg, switch (sc->type) { case CLOSURE_BSDF_DIFFUSE_ID: - label = bsdf_diffuse_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; #ifdef __SVM__ case CLOSURE_BSDF_OREN_NAYAR_ID: - label = bsdf_oren_nayar_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_oren_nayar_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; # ifdef __OSL__ case CLOSURE_BSDF_PHONG_RAMP_ID: - label = bsdf_phong_ramp_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_phong_ramp_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_DIFFUSE_RAMP_ID: - label = bsdf_diffuse_ramp_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_diffuse_ramp_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; # endif case CLOSURE_BSDF_TRANSLUCENT_ID: - label = bsdf_translucent_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_translucent_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_REFLECTION_ID: - label = bsdf_reflection_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_reflection_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_REFRACTION_ID: - label = bsdf_refraction_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_refraction_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_TRANSPARENT_ID: - label = bsdf_transparent_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_transparent_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_MICROFACET_GGX_ID: case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID: case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID: case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: - label = bsdf_microfacet_ggx_sample(kg, - sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_microfacet_ggx_sample(kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: - label = bsdf_microfacet_multi_ggx_sample(kg, - sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf, - &sd->lcg_state); + label = bsdf_microfacet_multi_ggx_sample( + kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, &sd->lcg_state); break; case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: - label = bsdf_microfacet_multi_ggx_glass_sample(kg, - sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf, - &sd->lcg_state); + label = bsdf_microfacet_multi_ggx_glass_sample( + kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf, &sd->lcg_state); break; case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID: - label = bsdf_microfacet_beckmann_sample(kg, - sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_microfacet_beckmann_sample( + kg, sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: - label = bsdf_ashikhmin_shirley_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_ashikhmin_shirley_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID: - label = bsdf_ashikhmin_velvet_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_ashikhmin_velvet_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_DIFFUSE_TOON_ID: - label = bsdf_diffuse_toon_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_diffuse_toon_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_GLOSSY_TOON_ID: - label = bsdf_glossy_toon_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_glossy_toon_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_HAIR_REFLECTION_ID: - label = bsdf_hair_reflection_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_hair_reflection_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: - label = bsdf_hair_transmission_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_hair_transmission_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: - label = bsdf_principled_hair_sample( - kg, sc, sd, randu, randv, eval, omega_in, &domega_in->dx, &domega_in->dy, pdf); + label = bsdf_principled_hair_sample(kg, sc, sd, randu, randv, eval, omega_in, pdf); break; # ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - label = bsdf_principled_diffuse_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_principled_diffuse_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: - label = bsdf_principled_sheen_sample(sc, - Ng, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + label = bsdf_principled_sheen_sample(sc, Ng, sd->I, randu, randv, eval, omega_in, pdf); break; # endif /* __PRINCIPLED__ */ #endif @@ -458,7 +233,7 @@ ccl_device #else ccl_device_inline #endif - float3 + Spectrum bsdf_eval(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private const ShaderClosure *sc, @@ -466,7 +241,7 @@ ccl_device_inline const bool is_transmission, ccl_private float *pdf) { - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); if (!is_transmission) { switch (sc->type) { diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h index 47066542122..75995262030 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h @@ -39,7 +39,7 @@ ccl_device_inline float bsdf_ashikhmin_shirley_roughness_to_exponent(float rough return 2.0f / (roughness * roughness) - 2.0f; } -ccl_device_forceinline float3 +ccl_device_forceinline Spectrum bsdf_ashikhmin_shirley_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, @@ -55,7 +55,7 @@ bsdf_ashikhmin_shirley_eval_reflect(ccl_private const ShaderClosure *sc, if (fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } if (NdotI > 0.0f && NdotO > 0.0f) { NdotI = fmaxf(NdotI, 1e-6f); @@ -105,16 +105,16 @@ bsdf_ashikhmin_shirley_eval_reflect(ccl_private const ShaderClosure *sc, } } - return make_float3(out, out, out); + return make_spectrum(out); } -ccl_device float3 bsdf_ashikhmin_shirley_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_ashikhmin_shirley_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x, @@ -133,14 +133,10 @@ ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x, ccl_device int bsdf_ashikhmin_shirley_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; @@ -214,19 +210,13 @@ ccl_device int bsdf_ashikhmin_shirley_sample(ccl_private const ShaderClosure *sc if (fmaxf(bsdf->alpha_x, bsdf->alpha_y) <= 1e-4f) { /* Some high number for MIS. */ *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); + *eval = make_spectrum(1e6f); label = LABEL_REFLECT | LABEL_SINGULAR; } else { /* leave the rest to eval_reflect */ *eval = bsdf_ashikhmin_shirley_eval_reflect(sc, I, *omega_in, pdf); } - -#ifdef __RAY_DIFFERENTIALS__ - /* just do the reflection thing for now */ - *domega_in_dx = (2.0f * dot(N, dIdx)) * N - dIdx; - *domega_in_dy = (2.0f * dot(N, dIdy)) * N - dIdy; -#endif } return label; diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h index 3d7906eef7d..9e68ea5d5e5 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h @@ -31,10 +31,10 @@ ccl_device int bsdf_ashikhmin_velvet_setup(ccl_private VelvetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_ashikhmin_velvet_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const VelvetBsdf *bsdf = (ccl_private const VelvetBsdf *)sc; float m_invsigma2 = bsdf->invsigma2; @@ -50,7 +50,7 @@ ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(ccl_private const ShaderClo if (!(fabsf(cosNH) < 1.0f - 1e-5f && cosHO > 1e-5f)) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } float cosNHdivHO = cosNH / cosHO; cosNHdivHO = fmaxf(cosNHdivHO, 1e-5f); @@ -68,33 +68,29 @@ ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(ccl_private const ShaderClo float out = 0.25f * (D * G) / cosNO; *pdf = 0.5f * M_1_PI_F; - return make_float3(out, out, out); + return make_spectrum(out); } *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_ashikhmin_velvet_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_ashikhmin_velvet_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_ashikhmin_velvet_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const VelvetBsdf *bsdf = (ccl_private const VelvetBsdf *)sc; @@ -129,22 +125,16 @@ ccl_device int bsdf_ashikhmin_velvet_sample(ccl_private const ShaderClosure *sc, float power = 0.25f * (D * G) / cosNO; - *eval = make_float3(power, power, power); - -#ifdef __RAY_DIFFERENTIALS__ - // TODO: find a better approximation for the retroreflective bounce - *domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx; - *domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy; -#endif + *eval = make_spectrum(power); } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } return LABEL_REFLECT | LABEL_DIFFUSE; } diff --git a/intern/cycles/kernel/closure/bsdf_diffuse.h b/intern/cycles/kernel/closure/bsdf_diffuse.h index 759ad03f8e8..ec64c375666 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse.h @@ -26,39 +26,35 @@ ccl_device int bsdf_diffuse_setup(ccl_private DiffuseBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_diffuse_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_diffuse_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; float cos_pi = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F; *pdf = cos_pi; - return make_float3(cos_pi, cos_pi, cos_pi); + return make_spectrum(cos_pi); } -ccl_device float3 bsdf_diffuse_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_diffuse_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; @@ -68,16 +64,11 @@ ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc, sample_cos_hemisphere(N, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) > 0.0f) { - *eval = make_float3(*pdf, *pdf, *pdf); -#ifdef __RAY_DIFFERENTIALS__ - // TODO: find a better approximation for the diffuse bounce - *domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx; - *domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy; -#endif + *eval = make_spectrum(*pdf); } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } return LABEL_REFLECT | LABEL_DIFFUSE; } @@ -90,39 +81,35 @@ ccl_device int bsdf_translucent_setup(ccl_private DiffuseBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_translucent_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_translucent_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_translucent_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_translucent_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; float cos_pi = fmaxf(-dot(N, omega_in), 0.0f) * M_1_PI_F; *pdf = cos_pi; - return make_float3(cos_pi, cos_pi, cos_pi); + return make_spectrum(cos_pi); } ccl_device int bsdf_translucent_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; @@ -132,16 +119,11 @@ ccl_device int bsdf_translucent_sample(ccl_private const ShaderClosure *sc, // distribution over the hemisphere sample_cos_hemisphere(-N, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) < 0) { - *eval = make_float3(*pdf, *pdf, *pdf); -#ifdef __RAY_DIFFERENTIALS__ - // TODO: find a better approximation for the diffuse bounce - *domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx); - *domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy); -#endif + *eval = make_spectrum(*pdf); } else { *pdf = 0; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } return LABEL_TRANSMIT | LABEL_DIFFUSE; } diff --git a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h index aa4c091f587..d7faf5c9e9a 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h @@ -9,6 +9,7 @@ #pragma once #include "kernel/sample/mapping.h" +#include "kernel/util/color.h" CCL_NAMESPACE_BEGIN @@ -46,38 +47,34 @@ ccl_device void bsdf_diffuse_ramp_blur(ccl_private ShaderClosure *sc, float roug { } -ccl_device float3 bsdf_diffuse_ramp_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_diffuse_ramp_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc; float3 N = bsdf->N; float cos_pi = fmaxf(dot(N, omega_in), 0.0f); *pdf = cos_pi * M_1_PI_F; - return bsdf_diffuse_ramp_get_color(bsdf->colors, cos_pi) * M_1_PI_F; + return rgb_to_spectrum(bsdf_diffuse_ramp_get_color(bsdf->colors, cos_pi) * M_1_PI_F); } -ccl_device float3 bsdf_diffuse_ramp_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_diffuse_ramp_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_diffuse_ramp_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc; @@ -87,15 +84,11 @@ ccl_device int bsdf_diffuse_ramp_sample(ccl_private const ShaderClosure *sc, sample_cos_hemisphere(N, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) > 0.0f) { - *eval = bsdf_diffuse_ramp_get_color(bsdf->colors, *pdf * M_PI_F) * M_1_PI_F; -# ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = (2 * dot(N, dIdx)) * N - dIdx; - *domega_in_dy = (2 * dot(N, dIdy)) * N - dIdy; -# endif + *eval = rgb_to_spectrum(bsdf_diffuse_ramp_get_color(bsdf->colors, *pdf * M_PI_F) * M_1_PI_F); } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } return LABEL_REFLECT | LABEL_DIFFUSE; } diff --git a/intern/cycles/kernel/closure/bsdf_hair.h b/intern/cycles/kernel/closure/bsdf_hair.h index a136ed05800..a29f7c444ae 100644 --- a/intern/cycles/kernel/closure/bsdf_hair.h +++ b/intern/cycles/kernel/closure/bsdf_hair.h @@ -37,10 +37,10 @@ ccl_device int bsdf_hair_transmission_setup(ccl_private HairBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_hair_reflection_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_hair_reflection_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; @@ -61,7 +61,7 @@ ccl_device float3 bsdf_hair_reflection_eval_reflect(ccl_private const ShaderClos if (M_PI_2_F - fabsf(theta_i) < 0.001f || cosphi_i < 0.0f) { *pdf = 0.0f; - return make_float3(*pdf, *pdf, *pdf); + return zero_spectrum(); } float roughness1_inv = 1.0f / roughness1; @@ -81,31 +81,31 @@ ccl_device float3 bsdf_hair_reflection_eval_reflect(ccl_private const ShaderClos (2 * (t * t + roughness1 * roughness1) * (a_R - b_R) * costheta_i); *pdf = phi_pdf * theta_pdf; - return make_float3(*pdf, *pdf, *pdf); + return make_spectrum(*pdf); } -ccl_device float3 bsdf_hair_transmission_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_hair_transmission_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_hair_reflection_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_hair_reflection_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_hair_transmission_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_hair_transmission_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; @@ -125,7 +125,7 @@ ccl_device float3 bsdf_hair_transmission_eval_transmit(ccl_private const ShaderC if (M_PI_2_F - fabsf(theta_i) < 0.001f) { *pdf = 0.0f; - return make_float3(*pdf, *pdf, *pdf); + return zero_spectrum(); } float costheta_i = fast_cosf(theta_i); @@ -145,20 +145,16 @@ ccl_device float3 bsdf_hair_transmission_eval_transmit(ccl_private const ShaderC float phi_pdf = roughness2 / (c_TT * (p * p + roughness2 * roughness2)); *pdf = phi_pdf * theta_pdf; - return make_float3(*pdf, *pdf, *pdf); + return make_spectrum(*pdf); } ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; @@ -194,17 +190,11 @@ ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc, fast_sincosf(phi, &sinphi, &cosphi); *omega_in = (cosphi * costheta_i) * locy - (sinphi * costheta_i) * locx + (sintheta_i)*Tg; - // differentials - TODO: find a better approximation for the reflective bounce -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = 2 * dot(locy, dIdx) * locy - dIdx; - *domega_in_dy = 2 * dot(locy, dIdy) * locy - dIdy; -#endif - *pdf = fabsf(phi_pdf * theta_pdf); if (M_PI_2_F - fabsf(theta_i) < 0.001f) *pdf = 0.0f; - *eval = make_float3(*pdf, *pdf, *pdf); + *eval = make_spectrum(*pdf); return LABEL_REFLECT | LABEL_GLOSSY; } @@ -212,14 +202,10 @@ ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc, ccl_device int bsdf_hair_transmission_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; @@ -255,18 +241,12 @@ ccl_device int bsdf_hair_transmission_sample(ccl_private const ShaderClosure *sc fast_sincosf(phi, &sinphi, &cosphi); *omega_in = (cosphi * costheta_i) * locy - (sinphi * costheta_i) * locx + (sintheta_i)*Tg; - // differentials - TODO: find a better approximation for the transmission bounce -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = 2 * dot(locy, dIdx) * locy - dIdx; - *domega_in_dy = 2 * dot(locy, dIdy) * locy - dIdy; -#endif - *pdf = fabsf(phi_pdf * theta_pdf); if (M_PI_2_F - fabsf(theta_i) < 0.001f) { *pdf = 0.0f; } - *eval = make_float3(*pdf, *pdf, *pdf); + *eval = make_spectrum(*pdf); /* TODO(sergey): Should always be negative, but seems some precision issue * is involved here. diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h index e7f24b89458..2236bc62050 100644 --- a/intern/cycles/kernel/closure/bsdf_hair_principled.h +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -20,7 +20,7 @@ typedef struct PrincipledHairBSDF { SHADER_CLOSURE_BASE; /* Absorption coefficient. */ - float3 sigma; + Spectrum sigma; /* Variance of the underlying logistic distribution. */ float v; /* Scale factor of the underlying logistic distribution. */ @@ -166,12 +166,6 @@ ccl_device_inline float longitudinal_scattering( } } -/* Combine the three values using their luminances. */ -ccl_device_inline float4 combine_with_energy(KernelGlobals kg, float3 c) -{ - return make_float4(c.x, c.y, c.z, linear_rgb_to_gray(kg, c)); -} - #ifdef __HAIR__ /* Set up the hair closure. */ ccl_device int bsdf_principled_hair_setup(ccl_private ShaderData *sd, @@ -214,34 +208,36 @@ ccl_device int bsdf_principled_hair_setup(ccl_private ShaderData *sd, #endif /* __HAIR__ */ /* Given the Fresnel term and transmittance, generate the attenuation terms for each bounce. */ -ccl_device_inline void hair_attenuation(KernelGlobals kg, - float f, - float3 T, - ccl_private float4 *Ap) +ccl_device_inline void hair_attenuation( + KernelGlobals kg, float f, Spectrum T, ccl_private Spectrum *Ap, ccl_private float *Ap_energy) { /* Primary specular (R). */ - Ap[0] = make_float4(f, f, f, f); + Ap[0] = make_spectrum(f); + Ap_energy[0] = f; /* Transmission (TT). */ - float3 col = sqr(1.0f - f) * T; - Ap[1] = combine_with_energy(kg, col); + Spectrum col = sqr(1.0f - f) * T; + Ap[1] = col; + Ap_energy[1] = spectrum_to_gray(kg, col); /* Secondary specular (TRT). */ col *= T * f; - Ap[2] = combine_with_energy(kg, col); + Ap[2] = col; + Ap_energy[2] = spectrum_to_gray(kg, col); /* Residual component (TRRT+). */ - col *= safe_divide_color(T * f, make_float3(1.0f, 1.0f, 1.0f) - T * f); - Ap[3] = combine_with_energy(kg, col); + col *= safe_divide(T * f, one_spectrum() - T * f); + Ap[3] = col; + Ap_energy[3] = spectrum_to_gray(kg, col); /* Normalize sampling weights. */ - float totweight = Ap[0].w + Ap[1].w + Ap[2].w + Ap[3].w; + float totweight = Ap_energy[0] + Ap_energy[1] + Ap_energy[2] + Ap_energy[3]; float fac = safe_divide(1.0f, totweight); - Ap[0].w *= fac; - Ap[1].w *= fac; - Ap[2].w *= fac; - Ap[3].w *= fac; + Ap_energy[0] *= fac; + Ap_energy[1] *= fac; + Ap_energy[2] *= fac; + Ap_energy[3] *= fac; } /* Given the tilt angle, generate the rotated theta_i for the different bounces. */ @@ -266,11 +262,11 @@ ccl_device_inline void hair_alpha_angles(float sin_theta_i, } /* Evaluation function for our shader. */ -ccl_device float3 bsdf_principled_hair_eval(KernelGlobals kg, - ccl_private const ShaderData *sd, - ccl_private const ShaderClosure *sc, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_principled_hair_eval(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderClosure *sc, + const float3 omega_in, + ccl_private float *pdf) { kernel_assert(isfinite_safe(sd->P) && isfinite_safe(sd->ray_length)); @@ -299,9 +295,11 @@ ccl_device float3 bsdf_principled_hair_eval(KernelGlobals kg, float cos_gamma_t = cos_from_sin(sin_gamma_t); float gamma_t = safe_asinf(sin_gamma_t); - float3 T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t)); - float4 Ap[4]; - hair_attenuation(kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap); + Spectrum T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t)); + Spectrum Ap[4]; + float Ap_energy[4]; + hair_attenuation( + kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap, Ap_energy); float sin_theta_i = wi.x; float cos_theta_i = cos_from_sin(sin_theta_i); @@ -312,35 +310,40 @@ ccl_device float3 bsdf_principled_hair_eval(KernelGlobals kg, float angles[6]; hair_alpha_angles(sin_theta_i, cos_theta_i, bsdf->alpha, angles); - float4 F; + Spectrum F; + float F_energy; float Mp, Np; /* Primary specular (R). */ Mp = longitudinal_scattering(angles[0], angles[1], sin_theta_o, cos_theta_o, bsdf->m0_roughness); Np = azimuthal_scattering(phi, 0, bsdf->s, gamma_o, gamma_t); F = Ap[0] * Mp * Np; - kernel_assert(isfinite_safe(float4_to_float3(F))); + F_energy = Ap_energy[0] * Mp * Np; + kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy)); /* Transmission (TT). */ Mp = longitudinal_scattering(angles[2], angles[3], sin_theta_o, cos_theta_o, 0.25f * bsdf->v); Np = azimuthal_scattering(phi, 1, bsdf->s, gamma_o, gamma_t); F += Ap[1] * Mp * Np; - kernel_assert(isfinite_safe(float4_to_float3(F))); + F_energy += Ap_energy[1] * Mp * Np; + kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy)); /* Secondary specular (TRT). */ Mp = longitudinal_scattering(angles[4], angles[5], sin_theta_o, cos_theta_o, 4.0f * bsdf->v); Np = azimuthal_scattering(phi, 2, bsdf->s, gamma_o, gamma_t); F += Ap[2] * Mp * Np; - kernel_assert(isfinite_safe(float4_to_float3(F))); + F_energy += Ap_energy[2] * Mp * Np; + kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy)); /* Residual component (TRRT+). */ Mp = longitudinal_scattering(sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f * bsdf->v); Np = M_1_2PI_F; F += Ap[3] * Mp * Np; - kernel_assert(isfinite_safe(float4_to_float3(F))); + F_energy += Ap_energy[3] * Mp * Np; + kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy)); - *pdf = F.w; - return float4_to_float3(F); + *pdf = F_energy; + return F; } /* Sampling function for the hair shader. */ @@ -349,10 +352,8 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals kg, ccl_private ShaderData *sd, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc; @@ -385,16 +386,18 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals kg, float cos_gamma_t = cos_from_sin(sin_gamma_t); float gamma_t = safe_asinf(sin_gamma_t); - float3 T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t)); - float4 Ap[4]; - hair_attenuation(kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap); + Spectrum T = exp(-bsdf->sigma * (2.0f * cos_gamma_t / cos_theta_t)); + Spectrum Ap[4]; + float Ap_energy[4]; + hair_attenuation( + kg, fresnel_dielectric_cos(cos_theta_o * cos_gamma_o, bsdf->eta), T, Ap, Ap_energy); int p = 0; for (; p < 3; p++) { - if (u[0].x < Ap[p].w) { + if (u[0].x < Ap_energy[p]) { break; } - u[0].x -= Ap[p].w; + u[0].x -= Ap_energy[p]; } float v = bsdf->v; @@ -429,44 +432,43 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals kg, hair_alpha_angles(sin_theta_i, cos_theta_i, bsdf->alpha, angles); - float4 F; + Spectrum F; + float F_energy; float Mp, Np; /* Primary specular (R). */ Mp = longitudinal_scattering(angles[0], angles[1], sin_theta_o, cos_theta_o, bsdf->m0_roughness); Np = azimuthal_scattering(phi, 0, bsdf->s, gamma_o, gamma_t); F = Ap[0] * Mp * Np; - kernel_assert(isfinite_safe(float4_to_float3(F))); + F_energy = Ap_energy[0] * Mp * Np; + kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy)); /* Transmission (TT). */ Mp = longitudinal_scattering(angles[2], angles[3], sin_theta_o, cos_theta_o, 0.25f * bsdf->v); Np = azimuthal_scattering(phi, 1, bsdf->s, gamma_o, gamma_t); F += Ap[1] * Mp * Np; - kernel_assert(isfinite_safe(float4_to_float3(F))); + F_energy += Ap_energy[1] * Mp * Np; + kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy)); /* Secondary specular (TRT). */ Mp = longitudinal_scattering(angles[4], angles[5], sin_theta_o, cos_theta_o, 4.0f * bsdf->v); Np = azimuthal_scattering(phi, 2, bsdf->s, gamma_o, gamma_t); F += Ap[2] * Mp * Np; - kernel_assert(isfinite_safe(float4_to_float3(F))); + F_energy += Ap_energy[2] * Mp * Np; + kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy)); /* Residual component (TRRT+). */ Mp = longitudinal_scattering(sin_theta_i, cos_theta_i, sin_theta_o, cos_theta_o, 4.0f * bsdf->v); Np = M_1_2PI_F; F += Ap[3] * Mp * Np; - kernel_assert(isfinite_safe(float4_to_float3(F))); + F_energy += Ap_energy[3] * Mp * Np; + kernel_assert(isfinite_safe(F) && isfinite_safe(F_energy)); - *eval = float4_to_float3(F); - *pdf = F.w; + *eval = F; + *pdf = F_energy; *omega_in = X * sin_theta_i + Y * cos_theta_i * cosf(phi_i) + Z * cos_theta_i * sinf(phi_i); -#ifdef __RAY_DIFFERENTIALS__ - float3 N = safe_normalize(sd->I + *omega_in); - *domega_in_dx = (2 * dot(N, sd->dI.dx)) * N - sd->dI.dx; - *domega_in_dy = (2 * dot(N, sd->dI.dy)) * N - sd->dI.dy; -#endif - return LABEL_GLOSSY | ((p == 0) ? LABEL_REFLECT : LABEL_TRANSMIT); } @@ -489,25 +491,28 @@ ccl_device_inline float bsdf_principled_hair_albedo_roughness_scale( return (((((0.245f * x) + 5.574f) * x - 10.73f) * x + 2.532f) * x - 0.215f) * x + 5.969f; } -ccl_device float3 bsdf_principled_hair_albedo(ccl_private const ShaderClosure *sc) +ccl_device Spectrum bsdf_principled_hair_albedo(ccl_private const ShaderClosure *sc) { ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc; return exp(-sqrt(bsdf->sigma) * bsdf_principled_hair_albedo_roughness_scale(bsdf->v)); } -ccl_device_inline float3 -bsdf_principled_hair_sigma_from_reflectance(const float3 color, const float azimuthal_roughness) +ccl_device_inline Spectrum +bsdf_principled_hair_sigma_from_reflectance(const Spectrum color, const float azimuthal_roughness) { - const float3 sigma = log(color) / - bsdf_principled_hair_albedo_roughness_scale(azimuthal_roughness); + const Spectrum sigma = log(color) / + bsdf_principled_hair_albedo_roughness_scale(azimuthal_roughness); return sigma * sigma; } -ccl_device_inline float3 bsdf_principled_hair_sigma_from_concentration(const float eumelanin, - const float pheomelanin) +ccl_device_inline Spectrum bsdf_principled_hair_sigma_from_concentration(const float eumelanin, + const float pheomelanin) { - return eumelanin * make_float3(0.506f, 0.841f, 1.653f) + - pheomelanin * make_float3(0.343f, 0.733f, 1.924f); + const float3 eumelanin_color = make_float3(0.506f, 0.841f, 1.653f); + const float3 pheomelanin_color = make_float3(0.343f, 0.733f, 1.924f); + + return eumelanin * rgb_to_spectrum(eumelanin_color) + + pheomelanin * rgb_to_spectrum(pheomelanin_color); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index c6cbd1ffae7..04d5ca90bfd 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -17,8 +17,8 @@ CCL_NAMESPACE_BEGIN typedef struct MicrofacetExtra { - float3 color, cspec0; - float3 fresnel_color; + Spectrum color, cspec0; + Spectrum fresnel_color; float clearcoat; } MicrofacetExtra; @@ -233,11 +233,11 @@ ccl_device_forceinline float3 microfacet_sample_stretched(KernelGlobals kg, * * Else it is simply white */ -ccl_device_forceinline float3 reflection_color(ccl_private const MicrofacetBsdf *bsdf, - float3 L, - float3 H) +ccl_device_forceinline Spectrum reflection_color(ccl_private const MicrofacetBsdf *bsdf, + float3 L, + float3 H) { - float3 F = make_float3(1.0f, 1.0f, 1.0f); + Spectrum F = one_spectrum(); bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID); if (use_fresnel) { @@ -357,10 +357,10 @@ ccl_device void bsdf_microfacet_ggx_blur(ccl_private ShaderClosure *sc, float ro bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); } -ccl_device float3 bsdf_microfacet_ggx_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_microfacet_ggx_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; @@ -370,7 +370,7 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(ccl_private const ShaderClosu if (m_refractive || alpha_x * alpha_y <= 1e-7f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } float cosNO = dot(N, I); @@ -451,12 +451,12 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(ccl_private const ShaderClosu /* eq. 20 */ float common = D * 0.25f / cosNO; - float3 F = reflection_color(bsdf, omega_in, m); + Spectrum F = reflection_color(bsdf, omega_in, m); if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) { F *= 0.25f * bsdf->extra->clearcoat; } - float3 out = F * G * common; + Spectrum out = F * G * common; /* eq. 2 in distribution of visible normals sampling * `pm = Dw = G1o * dot(m, I) * D / dot(N, I);` */ @@ -469,13 +469,13 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(ccl_private const ShaderClosu return out; } - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; @@ -486,7 +486,7 @@ ccl_device float3 bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClos if (!m_refractive || alpha_x * alpha_y <= 1e-7f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } float cosNO = dot(N, I); @@ -494,7 +494,7 @@ ccl_device float3 bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClos if (cosNO <= 0 || cosNI >= 0) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); /* vectors on same side -- not possible */ + return zero_spectrum(); /* vectors on same side -- not possible */ } /* compute half-vector of the refraction (eq. 16) */ float3 ht = -(m_eta * omega_in + I); @@ -530,21 +530,17 @@ ccl_device float3 bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClos float out = G * fabsf(cosHI * cosHO) * common; *pdf = G1o * fabsf(cosHO * cosHI) * common; - return make_float3(out, out, out); + return make_spectrum(out); } ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; @@ -588,7 +584,7 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, if (alpha_x * alpha_y <= 1e-7f) { /* some high number for MIS */ *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); + *eval = make_spectrum(1e6f); bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID || bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID); @@ -664,7 +660,7 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, float common = (G1o * D) * 0.25f / cosNO; *pdf = common; - float3 F = reflection_color(bsdf, *omega_in, m); + Spectrum F = reflection_color(bsdf, *omega_in, m); *eval = G1i * common * F; } @@ -672,14 +668,9 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, if (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) { *eval *= 0.25f * bsdf->extra->clearcoat; } - -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = (2 * dot(m, dIdx)) * m - dIdx; - *domega_in_dy = (2 * dot(m, dIdy)) * m - dIdy; -#endif } else { - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); *pdf = 0.0f; } } @@ -690,39 +681,18 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, /* CAUTION: the i and o variables are inverted relative to the paper * eq. 39 - compute actual refractive direction */ float3 R, T; -#ifdef __RAY_DIFFERENTIALS__ - float3 dRdx, dRdy, dTdx, dTdy; -#endif float m_eta = bsdf->ior, fresnel; bool inside; - fresnel = fresnel_dielectric(m_eta, - m, - I, - &R, - &T, -#ifdef __RAY_DIFFERENTIALS__ - dIdx, - dIdy, - &dRdx, - &dRdy, - &dTdx, - &dTdy, -#endif - &inside); + fresnel = fresnel_dielectric(m_eta, m, I, &R, &T, &inside); if (!inside && fresnel != 1.0f) { - *omega_in = T; -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = dTdx; - *domega_in_dy = dTdy; -#endif if (alpha_x * alpha_y <= 1e-7f || fabsf(m_eta - 1.0f) < 1e-4f) { /* some high number for MIS */ *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); + *eval = make_spectrum(1e6f); label = LABEL_TRANSMIT | LABEL_SINGULAR; } else { @@ -750,11 +720,11 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, float out = G1i * fabsf(cosHI * cosHO) * common; *pdf = cosHO * fabsf(cosHI) * common; - *eval = make_float3(out, out, out); + *eval = make_spectrum(out); } } else { - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); *pdf = 0.0f; } } @@ -835,10 +805,10 @@ ccl_device_inline float bsdf_beckmann_aniso_G1( return ((2.181f * a + 3.535f) * a) / ((2.577f * a + 2.276f) * a + 1.0f); } -ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_microfacet_beckmann_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; @@ -848,7 +818,7 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(ccl_private const Shader if (m_refractive || alpha_x * alpha_y <= 1e-7f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } float cosNO = dot(N, I); @@ -910,16 +880,16 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(ccl_private const Shader * pdf = pm * 0.25 / dot(m, I); */ *pdf = G1o * common; - return make_float3(out, out, out); + return make_spectrum(out); } - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_microfacet_beckmann_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; @@ -930,7 +900,7 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(ccl_private const Shade if (!m_refractive || alpha_x * alpha_y <= 1e-7f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } float cosNO = dot(N, I); @@ -938,7 +908,7 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(ccl_private const Shade if (cosNO <= 0 || cosNI >= 0) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } /* compute half-vector of the refraction (eq. 16) */ float3 ht = -(m_eta * omega_in + I); @@ -971,21 +941,17 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(ccl_private const Shade float out = G * fabsf(cosHI * cosHO) * common; *pdf = G1o * fabsf(cosHO * cosHI) * common; - return make_float3(out, out, out); + return make_spectrum(out); } ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; @@ -1028,7 +994,7 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg, if (alpha_x * alpha_y <= 1e-7f) { /* some high number for MIS */ *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); + *eval = make_spectrum(1e6f); label = LABEL_REFLECT | LABEL_SINGULAR; } else { @@ -1074,16 +1040,11 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg, float out = G * common; *pdf = G1o * common; - *eval = make_float3(out, out, out); + *eval = make_spectrum(out); } - -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = (2 * dot(m, dIdx)) * m - dIdx; - *domega_in_dy = (2 * dot(m, dIdy)) * m - dIdy; -#endif } else { - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); *pdf = 0.0f; } } @@ -1094,39 +1055,18 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg, /* CAUTION: the i and o variables are inverted relative to the paper * eq. 39 - compute actual refractive direction */ float3 R, T; -#ifdef __RAY_DIFFERENTIALS__ - float3 dRdx, dRdy, dTdx, dTdy; -#endif float m_eta = bsdf->ior, fresnel; bool inside; - fresnel = fresnel_dielectric(m_eta, - m, - I, - &R, - &T, -#ifdef __RAY_DIFFERENTIALS__ - dIdx, - dIdy, - &dRdx, - &dRdy, - &dTdx, - &dTdy, -#endif - &inside); + fresnel = fresnel_dielectric(m_eta, m, I, &R, &T, &inside); if (!inside && fresnel != 1.0f) { *omega_in = T; -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = dTdx; - *domega_in_dy = dTdy; -#endif - if (alpha_x * alpha_y <= 1e-7f || fabsf(m_eta - 1.0f) < 1e-4f) { /* some high number for MIS */ *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); + *eval = make_spectrum(1e6f); label = LABEL_TRANSMIT | LABEL_SINGULAR; } else { @@ -1155,11 +1095,11 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg, float out = G * fabsf(cosHI * cosHO) * common; *pdf = G1o * cosHO * fabsf(cosHI) * common; - *eval = make_float3(out, out, out); + *eval = make_spectrum(out); } } else { - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); *pdf = 0.0f; } } diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index b2e068daf17..ac37a648a2c 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -95,29 +95,29 @@ ccl_device_forceinline float3 mf_sample_vndf(const float3 wi, /* Phase function for reflective materials. */ ccl_device_forceinline float3 mf_sample_phase_glossy(const float3 wi, - ccl_private float3 *weight, + ccl_private Spectrum *weight, const float3 wm) { return -wi + 2.0f * wm * dot(wi, wm); } -ccl_device_forceinline float3 mf_eval_phase_glossy(const float3 w, - const float lambda, - const float3 wo, - const float2 alpha) +ccl_device_forceinline Spectrum mf_eval_phase_glossy(const float3 w, + const float lambda, + const float3 wo, + const float2 alpha) { if (w.z > 0.9999f) - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); const float3 wh = normalize(wo - w); if (wh.z < 0.0f) - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); float pArea = (w.z < -0.9999f) ? 1.0f : lambda * w.z; const float dotW_WH = dot(-w, wh); if (dotW_WH < 0.0f) - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); float phase = max(0.0f, dotW_WH) * 0.25f / max(pArea * dotW_WH, 1e-7f); if (alpha.x == alpha.y) @@ -125,7 +125,7 @@ ccl_device_forceinline float3 mf_eval_phase_glossy(const float3 w, else phase *= D_ggx_aniso(wh, alpha); - return make_float3(phase, phase, phase); + return make_spectrum(phase); } /* Phase function for dielectric transmissive materials, including both reflection and refraction @@ -148,22 +148,22 @@ ccl_device_forceinline float3 mf_sample_phase_glass(const float3 wi, return normalize(wm * (cosI * inv_eta + cosT) - wi * inv_eta); } -ccl_device_forceinline float3 mf_eval_phase_glass(const float3 w, - const float lambda, - const float3 wo, - const bool wo_outside, - const float2 alpha, - const float eta) +ccl_device_forceinline Spectrum mf_eval_phase_glass(const float3 w, + const float lambda, + const float3 wo, + const bool wo_outside, + const float2 alpha, + const float eta) { if (w.z > 0.9999f) - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); float pArea = (w.z < -0.9999f) ? 1.0f : lambda * w.z; float v; if (wo_outside) { const float3 wh = normalize(wo - w); if (wh.z < 0.0f) - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); const float dotW_WH = dot(-w, wh); v = fresnel_dielectric_cos(dotW_WH, eta) * max(0.0f, dotW_WH) * D_ggx(wh, alpha.x) * 0.25f / @@ -175,14 +175,14 @@ ccl_device_forceinline float3 mf_eval_phase_glass(const float3 w, wh = -wh; const float dotW_WH = dot(-w, wh), dotWO_WH = dot(wo, wh); if (dotW_WH < 0.0f) - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); float temp = dotW_WH + eta * dotWO_WH; v = (1.0f - fresnel_dielectric_cos(dotW_WH, eta)) * max(0.0f, dotW_WH) * max(0.0f, -dotWO_WH) * D_ggx(wh, alpha.x) / (pArea * temp * temp); } - return make_float3(v, v, v); + return make_spectrum(v); } /* === Utility functions for the random walks === */ @@ -415,27 +415,27 @@ ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(ccl_private Microfacet return bsdf_microfacet_multi_ggx_common_setup(bsdf); } -ccl_device float3 bsdf_microfacet_multi_ggx_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf, - ccl_private uint *lcg_state) +ccl_device Spectrum bsdf_microfacet_multi_ggx_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf, + ccl_private uint *lcg_state) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf, - ccl_private uint *lcg_state) +ccl_device Spectrum bsdf_microfacet_multi_ggx_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf, + ccl_private uint *lcg_state) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } float3 X, Y, Z; @@ -444,7 +444,7 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(ccl_private const Shade /* Ensure that the both directions are on the outside w.r.t. the shading normal. */ if (dot(Z, I) <= 0.0f || dot(Z, omega_in) <= 0.0f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID); @@ -478,14 +478,10 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf, ccl_private uint *lcg_state) { @@ -509,11 +505,7 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg, return LABEL_NONE; } *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx; - *domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy; -#endif + *eval = make_spectrum(1e6f); return LABEL_REFLECT | LABEL_SINGULAR; } @@ -551,10 +543,6 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg, *pdf = mf_ggx_pdf(localI, localO, bsdf->alpha_x); *eval *= *pdf; -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx; - *domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy; -#endif return LABEL_REFLECT | LABEL_GLOSSY; } @@ -588,7 +576,7 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(ccl_private Microfa return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -ccl_device float3 +ccl_device Spectrum bsdf_microfacet_multi_ggx_glass_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, @@ -599,7 +587,7 @@ bsdf_microfacet_multi_ggx_glass_eval_transmit(ccl_private const ShaderClosure *s if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } float3 X, Y, Z; @@ -622,17 +610,18 @@ bsdf_microfacet_multi_ggx_glass_eval_transmit(ccl_private const ShaderClosure *s bsdf->extra->color); } -ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf, - ccl_private uint *lcg_state) +ccl_device Spectrum +bsdf_microfacet_multi_ggx_glass_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf, + ccl_private uint *lcg_state) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID); @@ -661,14 +650,10 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg, ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf, ccl_private uint *lcg_state) { @@ -679,41 +664,17 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg, if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { float3 R, T; -#ifdef __RAY_DIFFERENTIALS__ - float3 dRdx, dRdy, dTdx, dTdy; -#endif bool inside; - float fresnel = fresnel_dielectric(bsdf->ior, - Z, - I, - &R, - &T, -#ifdef __RAY_DIFFERENTIALS__ - dIdx, - dIdy, - &dRdx, - &dRdy, - &dTdx, - &dTdy, -#endif - &inside); + float fresnel = fresnel_dielectric(bsdf->ior, Z, I, &R, &T, &inside); *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); + *eval = make_spectrum(1e6f); if (randu < fresnel) { *omega_in = R; -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = dRdx; - *domega_in_dy = dRdy; -#endif return LABEL_REFLECT | LABEL_SINGULAR; } else { *omega_in = T; -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = dTdx; - *domega_in_dy = dTdy; -#endif return LABEL_TRANSMIT | LABEL_SINGULAR; } } @@ -739,22 +700,9 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg, *omega_in = X * localO.x + Y * localO.y + Z * localO.z; if (localO.z * localI.z > 0.0f) { -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = (2 * dot(Z, dIdx)) * Z - dIdx; - *domega_in_dy = (2 * dot(Z, dIdy)) * Z - dIdy; -#endif return LABEL_REFLECT | LABEL_GLOSSY; } else { -#ifdef __RAY_DIFFERENTIALS__ - float cosI = dot(Z, I); - float dnp = max(sqrtf(1.0f - (bsdf->ior * bsdf->ior * (1.0f - cosI * cosI))), 1e-7f); - *domega_in_dx = -(bsdf->ior * dIdx) + - ((bsdf->ior - bsdf->ior * bsdf->ior * cosI / dnp) * dot(dIdx, Z)) * Z; - *domega_in_dy = -(bsdf->ior * dIdy) + - ((bsdf->ior - bsdf->ior * bsdf->ior * cosI / dnp) * dot(dIdy, Z)) * Z; -#endif - return LABEL_TRANSMIT | LABEL_GLOSSY; } } diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h index e4fcf0e6ba3..91fb9158050 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h @@ -12,16 +12,16 @@ * multi-scattered energy is used. In combination with MIS, that is enough to produce an unbiased * result, although the balance heuristic isn't necessarily optimal anymore. */ -ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, - float3 wo, - const bool wo_outside, - const float3 color, - const float alpha_x, - const float alpha_y, - ccl_private uint *lcg_state, - const float eta, - bool use_fresnel, - const float3 cspec0) +ccl_device_forceinline Spectrum MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, + float3 wo, + const bool wo_outside, + const Spectrum color, + const float alpha_x, + const float alpha_y, + ccl_private uint *lcg_state, + const float eta, + bool use_fresnel, + const Spectrum cspec0) { /* Evaluating for a shallower incoming direction produces less noise, and the properties of the * BSDF guarantee reciprocity. */ @@ -46,7 +46,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, } if (wi.z < 1e-5f || (wo.z < 1e-5f && wo_outside) || (wo.z > -1e-5f && !wo_outside)) - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); const float2 alpha = make_float2(alpha_x, alpha_y); @@ -54,8 +54,8 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, float shadowing_lambda = mf_lambda(wo_outside ? wo : -wo, alpha); /* Analytically compute single scattering for lower noise. */ - float3 eval; - float3 throughput = make_float3(1.0f, 1.0f, 1.0f); + Spectrum eval; + Spectrum throughput = one_spectrum(); const float3 wh = normalize(wi + wo); #ifdef MF_MULTI_GLASS eval = mf_eval_phase_glass(-wi, lambda_r, wo, wo_outside, alpha, eta); @@ -70,7 +70,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, val *= D_ggx(wh, alpha.x); else val *= D_ggx_aniso(wh, alpha); - eval = make_float3(val, val, val); + eval = make_spectrum(val); #endif float F0 = fresnel_dielectric_cos(1.0f, eta); @@ -99,7 +99,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, #ifdef MF_MULTI_GLASS if (order == 0 && use_fresnel) { /* Evaluate amount of scattering towards wo on this microfacet. */ - float3 phase; + Spectrum phase; if (outside) phase = mf_eval_phase_glass(wr, lambda_r, wo, wo_outside, alpha, eta); else @@ -113,7 +113,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, #endif if (order > 0) { /* Evaluate amount of scattering towards wo on this microfacet. */ - float3 phase; + Spectrum phase; #ifdef MF_MULTI_GLASS if (outside) phase = mf_eval_phase_glass(wr, lambda_r, wo, wo_outside, alpha, eta); @@ -172,19 +172,19 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, * walk escaped the surface in wo. The function returns the throughput between wi and wo. Without * reflection losses due to coloring or fresnel absorption in conductors, the sampling is optimal. */ -ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, - ccl_private float3 *wo, - const float3 color, - const float alpha_x, - const float alpha_y, - ccl_private uint *lcg_state, - const float eta, - bool use_fresnel, - const float3 cspec0) +ccl_device_forceinline Spectrum MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, + ccl_private float3 *wo, + const Spectrum color, + const float alpha_x, + const float alpha_y, + ccl_private uint *lcg_state, + const float eta, + bool use_fresnel, + const Spectrum cspec0) { const float2 alpha = make_float2(alpha_x, alpha_y); - float3 throughput = make_float3(1.0f, 1.0f, 1.0f); + Spectrum throughput = one_spectrum(); float3 wr = -wi; float lambda_r = mf_lambda(wr, alpha); float hr = 1.0f; @@ -229,7 +229,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, throughput *= color; } else { - float3 t_color = interpolate_fresnel_color(wi_prev, wm, eta, F0, cspec0); + Spectrum t_color = interpolate_fresnel_color(wi_prev, wm, eta, F0, cspec0); if (order == 0) throughput = t_color; @@ -239,7 +239,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, } #else /* MF_MULTI_GLOSSY */ if (use_fresnel) { - float3 t_color = interpolate_fresnel_color(-wr, wm, eta, F0, cspec0); + Spectrum t_color = interpolate_fresnel_color(-wr, wm, eta, F0, cspec0); if (order == 0) throughput = t_color; @@ -254,7 +254,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, G1_r = mf_G1(wr, C1_r, lambda_r); } *wo = make_float3(0.0f, 0.0f, 1.0f); - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } #undef MF_MULTI_GLASS diff --git a/intern/cycles/kernel/closure/bsdf_oren_nayar.h b/intern/cycles/kernel/closure/bsdf_oren_nayar.h index 56c7ec869c7..b85390f0676 100644 --- a/intern/cycles/kernel/closure/bsdf_oren_nayar.h +++ b/intern/cycles/kernel/closure/bsdf_oren_nayar.h @@ -15,10 +15,10 @@ typedef struct OrenNayarBsdf { static_assert(sizeof(ShaderClosure) >= sizeof(OrenNayarBsdf), "OrenNayarBsdf is too large!"); -ccl_device float3 bsdf_oren_nayar_get_intensity(ccl_private const ShaderClosure *sc, - float3 n, - float3 v, - float3 l) +ccl_device Spectrum bsdf_oren_nayar_get_intensity(ccl_private const ShaderClosure *sc, + float3 n, + float3 v, + float3 l) { ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; float nl = max(dot(n, l), 0.0f); @@ -28,7 +28,7 @@ ccl_device float3 bsdf_oren_nayar_get_intensity(ccl_private const ShaderClosure if (t > 0.0f) t /= max(nl, nv) + FLT_MIN; float is = nl * (bsdf->a + bsdf->b * t); - return make_float3(is, is, is); + return make_spectrum(is); } ccl_device int bsdf_oren_nayar_setup(ccl_private OrenNayarBsdf *bsdf) @@ -47,10 +47,10 @@ ccl_device int bsdf_oren_nayar_setup(ccl_private OrenNayarBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_oren_nayar_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_oren_nayar_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; if (dot(bsdf->N, omega_in) > 0.0f) { @@ -59,30 +59,26 @@ ccl_device float3 bsdf_oren_nayar_eval_reflect(ccl_private const ShaderClosure * } else { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } } -ccl_device float3 bsdf_oren_nayar_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_oren_nayar_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_oren_nayar_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; @@ -90,16 +86,10 @@ ccl_device int bsdf_oren_nayar_sample(ccl_private const ShaderClosure *sc, if (dot(Ng, *omega_in) > 0.0f) { *eval = bsdf_oren_nayar_get_intensity(sc, bsdf->N, I, *omega_in); - -#ifdef __RAY_DIFFERENTIALS__ - // TODO: find a better approximation for the bounce - *domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx; - *domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy; -#endif } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } return LABEL_REFLECT | LABEL_DIFFUSE; diff --git a/intern/cycles/kernel/closure/bsdf_phong_ramp.h b/intern/cycles/kernel/closure/bsdf_phong_ramp.h index 74a1f7ae090..4236e77ae6c 100644 --- a/intern/cycles/kernel/closure/bsdf_phong_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_phong_ramp.h @@ -8,6 +8,8 @@ #pragma once +#include "kernel/util/color.h" + CCL_NAMESPACE_BEGIN #ifdef __OSL__ @@ -42,10 +44,10 @@ ccl_device int bsdf_phong_ramp_setup(ccl_private PhongRampBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_phong_ramp_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_phong_ramp_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc; float m_exponent = bsdf->exponent; @@ -61,11 +63,11 @@ ccl_device float3 bsdf_phong_ramp_eval_reflect(ccl_private const ShaderClosure * float common = 0.5f * M_1_PI_F * cosp; float out = cosNI * (m_exponent + 2) * common; *pdf = (m_exponent + 1) * common; - return bsdf_phong_ramp_get_color(bsdf->colors, cosp) * out; + return rgb_to_spectrum(bsdf_phong_ramp_get_color(bsdf->colors, cosp) * out); } } *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device float3 bsdf_phong_ramp_eval_transmit(ccl_private const ShaderClosure *sc, @@ -80,14 +82,10 @@ ccl_device float3 bsdf_phong_ramp_eval_transmit(ccl_private const ShaderClosure ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc; @@ -97,12 +95,6 @@ ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc, if (cosNO > 0) { // reflect the view vector float3 R = (2 * cosNO) * bsdf->N - I; - -# ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = (2 * dot(bsdf->N, dIdx)) * bsdf->N - dIdx; - *domega_in_dy = (2 * dot(bsdf->N, dIdy)) * bsdf->N - dIdy; -# endif - float3 T, B; make_orthonormals(R, &T, &B); float phi = M_2PI_F * randu; @@ -119,12 +111,12 @@ ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc, float common = 0.5f * M_1_PI_F * cosp; *pdf = (m_exponent + 1) * common; float out = cosNI * (m_exponent + 2) * common; - *eval = bsdf_phong_ramp_get_color(bsdf->colors, cosp) * out; + *eval = rgb_to_spectrum(bsdf_phong_ramp_get_color(bsdf->colors, cosp) * out); } } } else { - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); *pdf = 0.0f; } return LABEL_REFLECT | LABEL_GLOSSY; diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index 5a7020e82d2..39cca1bd970 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -42,7 +42,7 @@ ccl_device int bsdf_principled_diffuse_setup(ccl_private PrincipledDiffuseBsdf * return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 +ccl_device Spectrum bsdf_principled_diffuse_compute_brdf(ccl_private const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, @@ -52,7 +52,7 @@ bsdf_principled_diffuse_compute_brdf(ccl_private const PrincipledDiffuseBsdf *bs const float NdotL = dot(N, L); if (NdotL <= 0) { - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } const float NdotV = dot(N, V); @@ -82,7 +82,7 @@ bsdf_principled_diffuse_compute_brdf(ccl_private const PrincipledDiffuseBsdf *bs float value = M_1_PI_F * NdotL * f; - return make_float3(value, value, value); + return make_spectrum(value); } /* Compute Fresnel at entry point, to be combined with #PRINCIPLED_DIFFUSE_LAMBERT_EXIT @@ -109,10 +109,10 @@ ccl_device int bsdf_principled_diffuse_setup(ccl_private PrincipledDiffuseBsdf * return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_principled_diffuse_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_principled_diffuse_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)sc; @@ -126,30 +126,26 @@ ccl_device float3 bsdf_principled_diffuse_eval_reflect(ccl_private const ShaderC } else { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } } -ccl_device float3 bsdf_principled_diffuse_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_principled_diffuse_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_principled_diffuse_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)sc; @@ -160,16 +156,10 @@ ccl_device int bsdf_principled_diffuse_sample(ccl_private const ShaderClosure *s if (dot(Ng, *omega_in) > 0) { *eval = bsdf_principled_diffuse_compute_brdf(bsdf, N, I, *omega_in, pdf); - -#ifdef __RAY_DIFFERENTIALS__ - // TODO: find a better approximation for the diffuse bounce - *domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx); - *domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy); -#endif } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } return LABEL_REFLECT | LABEL_DIFFUSE; } diff --git a/intern/cycles/kernel/closure/bsdf_principled_sheen.h b/intern/cycles/kernel/closure/bsdf_principled_sheen.h index 3a96a93db73..fa46f47eb21 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_sheen.h +++ b/intern/cycles/kernel/closure/bsdf_principled_sheen.h @@ -32,7 +32,7 @@ ccl_device_inline float calculate_avg_principled_sheen_brdf(float3 N, float3 I) return schlick_fresnel(NdotI) * NdotI; } -ccl_device float3 +ccl_device Spectrum calculate_principled_sheen_brdf(float3 N, float3 V, float3 L, float3 H, ccl_private float *pdf) { float NdotL = dot(N, L); @@ -40,14 +40,14 @@ calculate_principled_sheen_brdf(float3 N, float3 V, float3 L, float3 H, ccl_priv if (NdotL < 0 || NdotV < 0) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } float LdotH = dot(L, H); float value = schlick_fresnel(LdotH) * NdotL; - return make_float3(value, value, value); + return make_spectrum(value); } ccl_device int bsdf_principled_sheen_setup(ccl_private const ShaderData *sd, @@ -59,10 +59,10 @@ ccl_device int bsdf_principled_sheen_setup(ccl_private const ShaderData *sd, return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_principled_sheen_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_principled_sheen_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const PrincipledSheenBsdf *bsdf = (ccl_private const PrincipledSheenBsdf *)sc; @@ -77,30 +77,26 @@ ccl_device float3 bsdf_principled_sheen_eval_reflect(ccl_private const ShaderClo } else { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } } -ccl_device float3 bsdf_principled_sheen_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_principled_sheen_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_principled_sheen_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const PrincipledSheenBsdf *bsdf = (ccl_private const PrincipledSheenBsdf *)sc; @@ -113,15 +109,9 @@ ccl_device int bsdf_principled_sheen_sample(ccl_private const ShaderClosure *sc, float3 H = normalize(I + *omega_in); *eval = calculate_principled_sheen_brdf(N, I, *omega_in, H, pdf); - -#ifdef __RAY_DIFFERENTIALS__ - // TODO: find a better approximation for the diffuse bounce - *domega_in_dx = -((2 * dot(N, dIdx)) * N - dIdx); - *domega_in_dy = -((2 * dot(N, dIdy)) * N - dIdy); -#endif } else { - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); *pdf = 0.0f; } return LABEL_REFLECT | LABEL_DIFFUSE; diff --git a/intern/cycles/kernel/closure/bsdf_reflection.h b/intern/cycles/kernel/closure/bsdf_reflection.h index c8db2b7cf13..5e6c6cdcde6 100644 --- a/intern/cycles/kernel/closure/bsdf_reflection.h +++ b/intern/cycles/kernel/closure/bsdf_reflection.h @@ -18,35 +18,31 @@ ccl_device int bsdf_reflection_setup(ccl_private MicrofacetBsdf *bsdf) return SD_BSDF; } -ccl_device float3 bsdf_reflection_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_reflection_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_reflection_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_reflection_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_reflection_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; @@ -57,18 +53,14 @@ ccl_device int bsdf_reflection_sample(ccl_private const ShaderClosure *sc, if (cosNO > 0) { *omega_in = (2 * cosNO) * N - I; if (dot(Ng, *omega_in) > 0) { -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = 2 * dot(N, dIdx) * N - dIdx; - *domega_in_dy = 2 * dot(N, dIdy) * N - dIdy; -#endif /* Some high number for MIS. */ *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); + *eval = make_spectrum(1e6f); } } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } return LABEL_REFLECT | LABEL_SINGULAR; } diff --git a/intern/cycles/kernel/closure/bsdf_refraction.h b/intern/cycles/kernel/closure/bsdf_refraction.h index 862e774da87..e680a9617db 100644 --- a/intern/cycles/kernel/closure/bsdf_refraction.h +++ b/intern/cycles/kernel/closure/bsdf_refraction.h @@ -18,35 +18,31 @@ ccl_device int bsdf_refraction_setup(ccl_private MicrofacetBsdf *bsdf) return SD_BSDF; } -ccl_device float3 bsdf_refraction_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_refraction_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_refraction_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_refraction_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_refraction_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; @@ -54,39 +50,19 @@ ccl_device int bsdf_refraction_sample(ccl_private const ShaderClosure *sc, float3 N = bsdf->N; float3 R, T; -#ifdef __RAY_DIFFERENTIALS__ - float3 dRdx, dRdy, dTdx, dTdy; -#endif bool inside; float fresnel; - fresnel = fresnel_dielectric(m_eta, - N, - I, - &R, - &T, -#ifdef __RAY_DIFFERENTIALS__ - dIdx, - dIdy, - &dRdx, - &dRdy, - &dTdx, - &dTdy, -#endif - &inside); + fresnel = fresnel_dielectric(m_eta, N, I, &R, &T, &inside); if (!inside && fresnel != 1.0f) { /* Some high number for MIS. */ *pdf = 1e6f; - *eval = make_float3(1e6f, 1e6f, 1e6f); + *eval = make_spectrum(1e6f); *omega_in = T; -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = dTdx; - *domega_in_dy = dTdy; -#endif } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } return LABEL_TRANSMIT | LABEL_SINGULAR; } diff --git a/intern/cycles/kernel/closure/bsdf_toon.h b/intern/cycles/kernel/closure/bsdf_toon.h index 0400fc61860..c9086823de9 100644 --- a/intern/cycles/kernel/closure/bsdf_toon.h +++ b/intern/cycles/kernel/closure/bsdf_toon.h @@ -30,7 +30,7 @@ ccl_device int bsdf_diffuse_toon_setup(ccl_private ToonBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_toon_get_intensity(float max_angle, float smooth, float angle) +ccl_device float bsdf_toon_get_intensity(float max_angle, float smooth, float angle) { float is; @@ -41,7 +41,7 @@ ccl_device float3 bsdf_toon_get_intensity(float max_angle, float smooth, float a else is = 0.0f; - return make_float3(is, is, is); + return is; } ccl_device float bsdf_toon_get_sample_angle(float max_angle, float smooth) @@ -49,48 +49,44 @@ ccl_device float bsdf_toon_get_sample_angle(float max_angle, float smooth) return fminf(max_angle + smooth, M_PI_2_F); } -ccl_device float3 bsdf_diffuse_toon_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_diffuse_toon_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float angle = safe_acosf(fmaxf(dot(bsdf->N, omega_in), 0.0f)); - float3 eval = bsdf_toon_get_intensity(max_angle, smooth, angle); + float eval = bsdf_toon_get_intensity(max_angle, smooth, angle); - if (eval.x > 0.0f) { + if (eval > 0.0f) { float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth); *pdf = 0.5f * M_1_PI_F / (1.0f - cosf(sample_angle)); - return *pdf * eval; + return make_spectrum(*pdf * eval); } *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_diffuse_toon_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_diffuse_toon_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_diffuse_toon_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; @@ -103,21 +99,15 @@ ccl_device int bsdf_diffuse_toon_sample(ccl_private const ShaderClosure *sc, sample_uniform_cone(bsdf->N, sample_angle, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) > 0.0f) { - *eval = *pdf * bsdf_toon_get_intensity(max_angle, smooth, angle); - -#ifdef __RAY_DIFFERENTIALS__ - // TODO: find a better approximation for the bounce - *domega_in_dx = (2.0f * dot(bsdf->N, dIdx)) * bsdf->N - dIdx; - *domega_in_dy = (2.0f * dot(bsdf->N, dIdy)) * bsdf->N - dIdy; -#endif + *eval = make_spectrum(*pdf * bsdf_toon_get_intensity(max_angle, smooth, angle)); } else { - *eval = make_float3(0.f, 0.f, 0.f); + *eval = zero_spectrum(); *pdf = 0.0f; } } else { - *eval = make_float3(0.f, 0.f, 0.f); + *eval = zero_spectrum(); *pdf = 0.0f; } @@ -135,10 +125,10 @@ ccl_device int bsdf_glossy_toon_setup(ccl_private ToonBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_glossy_toon_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_glossy_toon_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; @@ -153,36 +143,32 @@ ccl_device float3 bsdf_glossy_toon_eval_reflect(ccl_private const ShaderClosure float angle = safe_acosf(fmaxf(cosRI, 0.0f)); - float3 eval = bsdf_toon_get_intensity(max_angle, smooth, angle); + float eval = bsdf_toon_get_intensity(max_angle, smooth, angle); float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth); *pdf = 0.5f * M_1_PI_F / (1.0f - cosf(sample_angle)); - return *pdf * eval; + return make_spectrum(*pdf * eval); } *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_glossy_toon_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_glossy_toon_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_glossy_toon_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; @@ -204,21 +190,16 @@ ccl_device int bsdf_glossy_toon_sample(ccl_private const ShaderClosure *sc, /* make sure the direction we chose is still in the right hemisphere */ if (cosNI > 0) { - *eval = *pdf * bsdf_toon_get_intensity(max_angle, smooth, angle); - -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = (2 * dot(bsdf->N, dIdx)) * bsdf->N - dIdx; - *domega_in_dy = (2 * dot(bsdf->N, dIdy)) * bsdf->N - dIdy; -#endif + *eval = make_spectrum(*pdf * bsdf_toon_get_intensity(max_angle, smooth, angle)); } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } } else { *pdf = 0.0f; - *eval = make_float3(0.0f, 0.0f, 0.0f); + *eval = zero_spectrum(); } } diff --git a/intern/cycles/kernel/closure/bsdf_transparent.h b/intern/cycles/kernel/closure/bsdf_transparent.h index 636d9d664f2..c2aee1e1633 100644 --- a/intern/cycles/kernel/closure/bsdf_transparent.h +++ b/intern/cycles/kernel/closure/bsdf_transparent.h @@ -11,7 +11,7 @@ CCL_NAMESPACE_BEGIN ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd, - const float3 weight, + const Spectrum weight, uint32_t path_flag) { /* Check cutoff weight. */ @@ -59,45 +59,37 @@ ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd, } } -ccl_device float3 bsdf_transparent_eval_reflect(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_transparent_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } -ccl_device float3 bsdf_transparent_eval_transmit(ccl_private const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum bsdf_transparent_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { *pdf = 0.0f; - return make_float3(0.0f, 0.0f, 0.0f); + return zero_spectrum(); } ccl_device int bsdf_transparent_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { // only one direction is possible *omega_in = -I; -#ifdef __RAY_DIFFERENTIALS__ - *domega_in_dx = -dIdx; - *domega_in_dy = -dIdy; -#endif *pdf = 1; - *eval = make_float3(1, 1, 1); + *eval = one_spectrum(); return LABEL_TRANSMIT | LABEL_TRANSPARENT; } diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index e3b24d487f1..3c48b98fed9 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -15,14 +15,6 @@ ccl_device float fresnel_dielectric(float eta, const float3 I, ccl_private float3 *R, ccl_private float3 *T, -#ifdef __RAY_DIFFERENTIALS__ - const float3 dIdx, - const float3 dIdy, - ccl_private float3 *dRdx, - ccl_private float3 *dRdy, - ccl_private float3 *dTdx, - ccl_private float3 *dTdy, -#endif ccl_private bool *is_inside) { float cos = dot(N, I), neta; @@ -45,28 +37,16 @@ ccl_device float fresnel_dielectric(float eta, // compute reflection *R = (2 * cos) * Nn - I; -#ifdef __RAY_DIFFERENTIALS__ - *dRdx = (2 * dot(Nn, dIdx)) * Nn - dIdx; - *dRdy = (2 * dot(Nn, dIdy)) * Nn - dIdy; -#endif float arg = 1 - (neta * neta * (1 - (cos * cos))); if (arg < 0) { *T = make_float3(0.0f, 0.0f, 0.0f); -#ifdef __RAY_DIFFERENTIALS__ - *dTdx = make_float3(0.0f, 0.0f, 0.0f); - *dTdy = make_float3(0.0f, 0.0f, 0.0f); -#endif return 1; // total internal reflection } else { float dnp = max(sqrtf(arg), 1e-7f); float nK = (neta * cos) - dnp; *T = -(neta * I) + (nK * Nn); -#ifdef __RAY_DIFFERENTIALS__ - *dTdx = -(neta * dIdx) + ((neta - neta * neta * cos / dnp) * dot(dIdx, Nn)) * Nn; - *dTdy = -(neta * dIdy) + ((neta - neta * neta * cos / dnp) * dot(dIdy, Nn)) * Nn; -#endif // compute Fresnel terms float cosTheta1 = cos; // N.R float cosTheta2 = -dot(Nn, *T); @@ -110,8 +90,8 @@ ccl_device float schlick_fresnel(float u) } /* Calculate the fresnel color which is a blend between white and the F0 color (cspec0) */ -ccl_device_forceinline float3 -interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, float3 cspec0) +ccl_device_forceinline Spectrum +interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, Spectrum cspec0) { /* Calculate the fresnel interpolation factor * The value from fresnel_dielectric_cos(...) has to be normalized because @@ -121,7 +101,7 @@ interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, float3 cspec0 float FH = (fresnel_dielectric_cos(dot(L, H), ior) - F0) * F0_norm; /* Blend between white and a specular color with respect to the fresnel */ - return cspec0 * (1.0f - FH) + make_float3(1.0f, 1.0f, 1.0f) * FH; + return cspec0 * (1.0f - FH) + make_spectrum(FH); } ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index b87790f5f8a..cdd4d128c1f 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -8,8 +8,8 @@ CCL_NAMESPACE_BEGIN typedef struct Bssrdf { SHADER_CLOSURE_BASE; - float3 radius; - float3 albedo; + Spectrum radius; + Spectrum albedo; float roughness; float anisotropy; } Bssrdf; @@ -69,12 +69,13 @@ ccl_device void bssrdf_setup_radius(ccl_private Bssrdf *bssrdf, const float fourthirdA = (4.0f / 3.0f) * (1.0f + F_dr) / (1.0f - F_dr); /* From Jensen's `Fdr` ratio formula. */ - const float3 alpha_prime = make_float3( - bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.x, fourthirdA), - bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.y, fourthirdA), - bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.z, fourthirdA)); + Spectrum alpha_prime; + FOREACH_SPECTRUM_CHANNEL (i) { + GET_SPECTRUM_CHANNEL(alpha_prime, i) = bssrdf_dipole_compute_alpha_prime( + GET_SPECTRUM_CHANNEL(bssrdf->albedo, i), fourthirdA); + } - bssrdf->radius *= sqrt(3.0f * (one_float3() - alpha_prime)); + bssrdf->radius *= sqrt(3.0f * (one_spectrum() - alpha_prime)); } } @@ -98,7 +99,7 @@ ccl_device_inline float bssrdf_burley_fitting(float A) /* Scale mean free path length so it gives similar looking result * to Cubic and Gaussian models. */ -ccl_device_inline float3 bssrdf_burley_compatible_mfp(float3 r) +ccl_device_inline Spectrum bssrdf_burley_compatible_mfp(Spectrum r) { return 0.25f * M_1_PI_F * r; } @@ -106,11 +107,13 @@ ccl_device_inline float3 bssrdf_burley_compatible_mfp(float3 r) ccl_device void bssrdf_burley_setup(ccl_private Bssrdf *bssrdf) { /* Mean free path length. */ - const float3 l = bssrdf_burley_compatible_mfp(bssrdf->radius); + const Spectrum l = bssrdf_burley_compatible_mfp(bssrdf->radius); /* Surface albedo. */ - const float3 A = bssrdf->albedo; - const float3 s = make_float3( - bssrdf_burley_fitting(A.x), bssrdf_burley_fitting(A.y), bssrdf_burley_fitting(A.z)); + const Spectrum A = bssrdf->albedo; + Spectrum s; + FOREACH_SPECTRUM_CHANNEL (i) { + GET_SPECTRUM_CHANNEL(s, i) = bssrdf_burley_fitting(GET_SPECTRUM_CHANNEL(A, i)); + } bssrdf->radius = l / s; } @@ -198,22 +201,18 @@ ccl_device void bssrdf_burley_sample(const float d, *h = safe_sqrtf(Rm * Rm - r_ * r_); } -ccl_device float bssrdf_num_channels(const float3 radius) +ccl_device float bssrdf_num_channels(const Spectrum radius) { float channels = 0; - if (radius.x > 0.0f) { - channels += 1.0f; - } - if (radius.y > 0.0f) { - channels += 1.0f; - } - if (radius.z > 0.0f) { - channels += 1.0f; + FOREACH_SPECTRUM_CHANNEL (i) { + if (GET_SPECTRUM_CHANNEL(radius, i) > 0.0f) { + channels += 1.0f; + } } return channels; } -ccl_device void bssrdf_sample(const float3 radius, +ccl_device void bssrdf_sample(const Spectrum radius, float xi, ccl_private float *r, ccl_private float *h) @@ -224,39 +223,44 @@ ccl_device void bssrdf_sample(const float3 radius, /* Sample color channel and reuse random number. Only a subset of channels * may be used if their radius was too small to handle as BSSRDF. */ xi *= num_channels; - - if (xi < 1.0f) { - sampled_radius = (radius.x > 0.0f) ? radius.x : (radius.y > 0.0f) ? radius.y : radius.z; - } - else if (xi < 2.0f) { - xi -= 1.0f; - sampled_radius = (radius.x > 0.0f && radius.y > 0.0f) ? radius.y : radius.z; - } - else { - xi -= 2.0f; - sampled_radius = radius.z; + sampled_radius = 0.0f; + + float sum = 0.0f; + FOREACH_SPECTRUM_CHANNEL (i) { + const float channel_radius = GET_SPECTRUM_CHANNEL(radius, i); + if (channel_radius > 0.0f) { + const float next_sum = sum + 1.0f; + if (xi < next_sum) { + xi -= sum; + sampled_radius = channel_radius; + break; + } + sum = next_sum; + } } /* Sample BSSRDF. */ bssrdf_burley_sample(sampled_radius, xi, r, h); } -ccl_device_forceinline float3 bssrdf_eval(const float3 radius, float r) +ccl_device_forceinline Spectrum bssrdf_eval(const Spectrum radius, float r) { - return make_float3(bssrdf_burley_pdf(radius.x, r), - bssrdf_burley_pdf(radius.y, r), - bssrdf_burley_pdf(radius.z, r)); + Spectrum result; + FOREACH_SPECTRUM_CHANNEL (i) { + GET_SPECTRUM_CHANNEL(result, i) = bssrdf_burley_pdf(GET_SPECTRUM_CHANNEL(radius, i), r); + } + return result; } -ccl_device_forceinline float bssrdf_pdf(const float3 radius, float r) +ccl_device_forceinline float bssrdf_pdf(const Spectrum radius, float r) { - float3 pdf = bssrdf_eval(radius, r); - return (pdf.x + pdf.y + pdf.z) / bssrdf_num_channels(radius); + Spectrum pdf = bssrdf_eval(radius, r); + return reduce_add(pdf) / bssrdf_num_channels(radius); } /* Setup */ -ccl_device_inline ccl_private Bssrdf *bssrdf_alloc(ccl_private ShaderData *sd, float3 weight) +ccl_device_inline ccl_private Bssrdf *bssrdf_alloc(ccl_private ShaderData *sd, Spectrum weight) { ccl_private Bssrdf *bssrdf = (ccl_private Bssrdf *)closure_alloc( sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight); @@ -294,29 +298,19 @@ ccl_device int bssrdf_setup(ccl_private ShaderData *sd, } /* Verify if the radii are large enough to sample without precision issues. */ - int bssrdf_channels = 3; - float3 diffuse_weight = make_float3(0.0f, 0.0f, 0.0f); - - if (bssrdf->radius.x < BSSRDF_MIN_RADIUS) { - diffuse_weight.x = bssrdf->weight.x; - bssrdf->weight.x = 0.0f; - bssrdf->radius.x = 0.0f; - bssrdf_channels--; - } - if (bssrdf->radius.y < BSSRDF_MIN_RADIUS) { - diffuse_weight.y = bssrdf->weight.y; - bssrdf->weight.y = 0.0f; - bssrdf->radius.y = 0.0f; - bssrdf_channels--; - } - if (bssrdf->radius.z < BSSRDF_MIN_RADIUS) { - diffuse_weight.z = bssrdf->weight.z; - bssrdf->weight.z = 0.0f; - bssrdf->radius.z = 0.0f; - bssrdf_channels--; + int bssrdf_channels = SPECTRUM_CHANNELS; + Spectrum diffuse_weight = zero_spectrum(); + + FOREACH_SPECTRUM_CHANNEL (i) { + if (GET_SPECTRUM_CHANNEL(bssrdf->radius, i) < BSSRDF_MIN_RADIUS) { + GET_SPECTRUM_CHANNEL(diffuse_weight, i) = GET_SPECTRUM_CHANNEL(bssrdf->weight, i); + GET_SPECTRUM_CHANNEL(bssrdf->weight, i) = 0.0f; + GET_SPECTRUM_CHANNEL(bssrdf->radius, i) = 0.0f; + bssrdf_channels--; + } } - if (bssrdf_channels < 3) { + if (bssrdf_channels < SPECTRUM_CHANNELS) { /* Add diffuse BSDF if any radius too small. */ #ifdef __PRINCIPLED__ if (bssrdf->roughness != FLT_MAX) { diff --git a/intern/cycles/kernel/closure/emissive.h b/intern/cycles/kernel/closure/emissive.h index 03e19cbde21..d896721f77b 100644 --- a/intern/cycles/kernel/closure/emissive.h +++ b/intern/cycles/kernel/closure/emissive.h @@ -12,7 +12,7 @@ CCL_NAMESPACE_BEGIN /* BACKGROUND CLOSURE */ -ccl_device void background_setup(ccl_private ShaderData *sd, const float3 weight) +ccl_device void background_setup(ccl_private ShaderData *sd, const Spectrum weight) { if (sd->flag & SD_EMISSION) { sd->closure_emission_background += weight; @@ -25,7 +25,7 @@ ccl_device void background_setup(ccl_private ShaderData *sd, const float3 weight /* EMISSION CLOSURE */ -ccl_device void emission_setup(ccl_private ShaderData *sd, const float3 weight) +ccl_device void emission_setup(ccl_private ShaderData *sd, const Spectrum weight) { if (sd->flag & SD_EMISSION) { sd->closure_emission_background += weight; @@ -54,11 +54,11 @@ ccl_device void emissive_sample(const float3 Ng, /* todo: not implemented and used yet */ } -ccl_device float3 emissive_simple_eval(const float3 Ng, const float3 I) +ccl_device Spectrum emissive_simple_eval(const float3 Ng, const float3 I) { float res = emissive_pdf(Ng, I); - return make_float3(res, res, res); + return make_spectrum(res); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/closure/volume.h b/intern/cycles/kernel/closure/volume.h index ef414c7b821..9dbb5154457 100644 --- a/intern/cycles/kernel/closure/volume.h +++ b/intern/cycles/kernel/closure/volume.h @@ -7,7 +7,7 @@ CCL_NAMESPACE_BEGIN /* VOLUME EXTINCTION */ -ccl_device void volume_extinction_setup(ccl_private ShaderData *sd, float3 weight) +ccl_device void volume_extinction_setup(ccl_private ShaderData *sd, Spectrum weight) { if (sd->flag & SD_EXTINCTION) { sd->closure_transparent_extinction += weight; @@ -48,10 +48,10 @@ ccl_device int volume_henyey_greenstein_setup(ccl_private HenyeyGreensteinVolume return SD_SCATTER; } -ccl_device float3 volume_henyey_greenstein_eval_phase(ccl_private const ShaderVolumeClosure *svc, - const float3 I, - float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum volume_henyey_greenstein_eval_phase(ccl_private const ShaderVolumeClosure *svc, + const float3 I, + float3 omega_in, + ccl_private float *pdf) { float g = svc->g; @@ -64,7 +64,7 @@ ccl_device float3 volume_henyey_greenstein_eval_phase(ccl_private const ShaderVo *pdf = single_peaked_henyey_greenstein(cos_theta, g); } - return make_float3(*pdf, *pdf, *pdf); + return make_spectrum(*pdf); } ccl_device float3 @@ -101,37 +101,27 @@ henyey_greenstrein_sample(float3 D, float g, float randu, float randv, ccl_priva ccl_device int volume_henyey_greenstein_sample(ccl_private const ShaderVolumeClosure *svc, float3 I, - float3 dIdx, - float3 dIdy, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private float3 *domega_in_dx, - ccl_private float3 *domega_in_dy, ccl_private float *pdf) { float g = svc->g; /* note that I points towards the viewer and so is used negated */ *omega_in = henyey_greenstrein_sample(-I, g, randu, randv, pdf); - *eval = make_float3(*pdf, *pdf, *pdf); /* perfect importance sampling */ - -#ifdef __RAY_DIFFERENTIALS__ - /* todo: implement ray differential estimation */ - *domega_in_dx = make_float3(0.0f, 0.0f, 0.0f); - *domega_in_dy = make_float3(0.0f, 0.0f, 0.0f); -#endif + *eval = make_spectrum(*pdf); /* perfect importance sampling */ return LABEL_VOLUME_SCATTER; } /* VOLUME CLOSURE */ -ccl_device float3 volume_phase_eval(ccl_private const ShaderData *sd, - ccl_private const ShaderVolumeClosure *svc, - float3 omega_in, - ccl_private float *pdf) +ccl_device Spectrum volume_phase_eval(ccl_private const ShaderData *sd, + ccl_private const ShaderVolumeClosure *svc, + float3 omega_in, + ccl_private float *pdf) { return volume_henyey_greenstein_eval_phase(svc, sd->I, omega_in, pdf); } @@ -140,22 +130,11 @@ ccl_device int volume_phase_sample(ccl_private const ShaderData *sd, ccl_private const ShaderVolumeClosure *svc, float randu, float randv, - ccl_private float3 *eval, + ccl_private Spectrum *eval, ccl_private float3 *omega_in, - ccl_private differential3 *domega_in, ccl_private float *pdf) { - return volume_henyey_greenstein_sample(svc, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); + return volume_henyey_greenstein_sample(svc, sd->I, randu, randv, eval, omega_in, pdf); } /* Volume sampling utilities. */ @@ -164,45 +143,44 @@ ccl_device int volume_phase_sample(ccl_private const ShaderData *sd, * unnecessary work in volumes and subsurface scattering. */ #define VOLUME_THROUGHPUT_EPSILON 1e-6f -ccl_device float3 volume_color_transmittance(float3 sigma, float t) +ccl_device Spectrum volume_color_transmittance(Spectrum sigma, float t) { return exp(-sigma * t); } -ccl_device float volume_channel_get(float3 value, int channel) +ccl_device float volume_channel_get(Spectrum value, int channel) { - return (channel == 0) ? value.x : ((channel == 1) ? value.y : value.z); + return GET_SPECTRUM_CHANNEL(value, channel); } -ccl_device int volume_sample_channel(float3 albedo, - float3 throughput, +ccl_device int volume_sample_channel(Spectrum albedo, + Spectrum throughput, float rand, - ccl_private float3 *pdf) + ccl_private Spectrum *pdf) { /* Sample color channel proportional to throughput and single scattering * albedo, to significantly reduce noise with many bounce, following: * * "Practical and Controllable Subsurface Scattering for Production Path * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ - float3 weights = fabs(throughput * albedo); - float sum_weights = weights.x + weights.y + weights.z; + Spectrum weights = fabs(throughput * albedo); + float sum_weights = reduce_add(weights); if (sum_weights > 0.0f) { *pdf = weights / sum_weights; } else { - *pdf = make_float3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f); + *pdf = make_spectrum(1.0f / SPECTRUM_CHANNELS); } - if (rand < pdf->x) { - return 0; - } - else if (rand < pdf->x + pdf->y) { - return 1; - } - else { - return 2; + float pdf_sum = 0.0f; + FOREACH_SPECTRUM_CHANNEL (i) { + pdf_sum += GET_SPECTRUM_CHANNEL(*pdf, i); + if (rand < pdf_sum) { + return i; + } } + return SPECTRUM_CHANNELS - 1; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/cpu/compat.h b/intern/cycles/kernel/device/cpu/compat.h index 631e55e0d42..1e3e790ca1f 100644 --- a/intern/cycles/kernel/device/cpu/compat.h +++ b/intern/cycles/kernel/device/cpu/compat.h @@ -33,38 +33,4 @@ CCL_NAMESPACE_BEGIN #define kernel_assert(cond) assert(cond) -/* Macros to handle different memory storage on different devices */ - -#ifdef __KERNEL_SSE2__ -typedef vector3<sseb> sse3b; -typedef vector3<ssef> sse3f; -typedef vector3<ssei> sse3i; - -ccl_device_inline void print_sse3b(const char *label, sse3b &a) -{ - print_sseb(label, a.x); - print_sseb(label, a.y); - print_sseb(label, a.z); -} - -ccl_device_inline void print_sse3f(const char *label, sse3f &a) -{ - print_ssef(label, a.x); - print_ssef(label, a.y); - print_ssef(label, a.z); -} - -ccl_device_inline void print_sse3i(const char *label, sse3i &a) -{ - print_ssei(label, a.x); - print_ssei(label, a.y); - print_ssei(label, a.z); -} - -# if defined(__KERNEL_AVX__) || defined(__KERNEL_AVX2__) -typedef vector3<avxf> avx3f; -# endif - -#endif - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/metal/compat.h b/intern/cycles/kernel/device/metal/compat.h index 80ee8ef5b57..b20cfca9a9c 100644 --- a/intern/cycles/kernel/device/metal/compat.h +++ b/intern/cycles/kernel/device/metal/compat.h @@ -189,35 +189,46 @@ void kernel_gpu_##name::run(thread MetalKernelContext& context, \ } volume_write_lambda_pass{kg, this, state}; /* make_type definitions with Metal style element initializers */ -#ifdef make_float2 -# undef make_float2 -#endif -#ifdef make_float3 -# undef make_float3 -#endif -#ifdef make_float4 -# undef make_float4 -#endif -#ifdef make_int2 -# undef make_int2 -#endif -#ifdef make_int3 -# undef make_int3 -#endif -#ifdef make_int4 -# undef make_int4 -#endif -#ifdef make_uchar4 -# undef make_uchar4 -#endif - -#define make_float2(x, y) float2(x, y) -#define make_float3(x, y, z) float3(x, y, z) -#define make_float4(x, y, z, w) float4(x, y, z, w) -#define make_int2(x, y) int2(x, y) -#define make_int3(x, y, z) int3(x, y, z) -#define make_int4(x, y, z, w) int4(x, y, z, w) -#define make_uchar4(x, y, z, w) uchar4(x, y, z, w) +ccl_device_forceinline float2 make_float2(const float x, const float y) +{ + return float2(x, y); +} + +ccl_device_forceinline float3 make_float3(const float x, const float y, const float z) +{ + return float3(x, y, z); +} + +ccl_device_forceinline float4 make_float4(const float x, + const float y, + const float z, + const float w) +{ + return float4(x, y, z, w); +} + +ccl_device_forceinline int2 make_int2(const int x, const int y) +{ + return int2(x, y); +} + +ccl_device_forceinline int3 make_int3(const int x, const int y, const int z) +{ + return int3(x, y, z); +} + +ccl_device_forceinline int4 make_int4(const int x, const int y, const int z, const int w) +{ + return int4(x, y, z, w); +} + +ccl_device_forceinline uchar4 make_uchar4(const uchar x, + const uchar y, + const uchar z, + const uchar w) +{ + return uchar4(x, y, z, w); +} /* Math functions */ diff --git a/intern/cycles/kernel/device/oneapi/compat.h b/intern/cycles/kernel/device/oneapi/compat.h index d8234ee1400..5c49674f247 100644 --- a/intern/cycles/kernel/device/oneapi/compat.h +++ b/intern/cycles/kernel/device/oneapi/compat.h @@ -149,25 +149,13 @@ void oneapi_kernel_##name(KernelGlobalsGPU *ccl_restrict kg, \ /* clang-format on */ /* Types */ + /* It's not possible to use sycl types like sycl::float3, sycl::int3, etc - * because these types have different interfaces from blender version */ + * because these types have different interfaces from blender version. */ using uchar = unsigned char; using sycl::half; -struct float3 { - float x, y, z; -}; - -ccl_always_inline float3 make_float3(float x, float y, float z) -{ - return {x, y, z}; -} -ccl_always_inline float3 make_float3(float x) -{ - return {x, x, x}; -} - /* math functions */ #define fabsf(x) sycl::fabs((x)) #define copysignf(x, y) sycl::copysign((x), (y)) diff --git a/intern/cycles/kernel/film/accumulate.h b/intern/cycles/kernel/film/accumulate.h index 33c35a68ad0..97ec915a8ad 100644 --- a/intern/cycles/kernel/film/accumulate.h +++ b/intern/cycles/kernel/film/accumulate.h @@ -21,10 +21,10 @@ CCL_NAMESPACE_BEGIN ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval, const ClosureType closure_type, - float3 value) + Spectrum value) { - eval->diffuse = zero_float3(); - eval->glossy = zero_float3(); + eval->diffuse = zero_spectrum(); + eval->glossy = zero_spectrum(); if (CLOSURE_IS_BSDF_DIFFUSE(closure_type)) { eval->diffuse = value; @@ -38,7 +38,7 @@ ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval, ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval, const ClosureType closure_type, - float3 value) + Spectrum value) { if (CLOSURE_IS_BSDF_DIFFUSE(closure_type)) { eval->diffuse += value; @@ -62,26 +62,26 @@ ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, float value) eval->sum *= value; } -ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, float3 value) +ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, Spectrum value) { eval->diffuse *= value; eval->glossy *= value; eval->sum *= value; } -ccl_device_inline float3 bsdf_eval_sum(ccl_private const BsdfEval *eval) +ccl_device_inline Spectrum bsdf_eval_sum(ccl_private const BsdfEval *eval) { return eval->sum; } -ccl_device_inline float3 bsdf_eval_pass_diffuse_weight(ccl_private const BsdfEval *eval) +ccl_device_inline Spectrum bsdf_eval_pass_diffuse_weight(ccl_private const BsdfEval *eval) { /* Ratio of diffuse weight to recover proportions for writing to render pass. * We assume reflection, transmission and volume scatter to be exclusive. */ return safe_divide(eval->diffuse, eval->sum); } -ccl_device_inline float3 bsdf_eval_pass_glossy_weight(ccl_private const BsdfEval *eval) +ccl_device_inline Spectrum bsdf_eval_pass_glossy_weight(ccl_private const BsdfEval *eval) { /* Ratio of glossy weight to recover proportions for writing to render pass. * We assume reflection, transmission and volume scatter to be exclusive. */ @@ -95,7 +95,9 @@ ccl_device_inline float3 bsdf_eval_pass_glossy_weight(ccl_private const BsdfEval * to render buffers instead of using per-thread memory, and to avoid the * impact of clamping on other contributions. */ -ccl_device_forceinline void kernel_accum_clamp(KernelGlobals kg, ccl_private float3 *L, int bounce) +ccl_device_forceinline void kernel_accum_clamp(KernelGlobals kg, + ccl_private Spectrum *L, + int bounce) { #ifdef __KERNEL_DEBUG_NAN__ if (!isfinite_safe(*L)) { @@ -154,7 +156,7 @@ ccl_device_inline int kernel_accum_sample(KernelGlobals kg, ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, const int sample, - const float3 contribution, + const Spectrum contribution, ccl_global float *ccl_restrict buffer) { /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping @@ -167,9 +169,13 @@ ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, } if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { - kernel_write_pass_float4( - buffer + kernel_data.film.pass_adaptive_aux_buffer, - make_float4(contribution.x * 2.0f, contribution.y * 2.0f, contribution.z * 2.0f, 0.0f)); + const float3 contribution_rgb = spectrum_to_rgb(contribution); + + kernel_write_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer, + make_float4(contribution_rgb.x * 2.0f, + contribution_rgb.y * 2.0f, + contribution_rgb.z * 2.0f, + 0.0f)); } } @@ -186,7 +192,7 @@ ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, const uint32_t path_flag, - const float3 contribution, + const Spectrum contribution, ccl_global float *ccl_restrict buffer) { if (!kernel_data.integrator.has_shadow_catcher) { @@ -198,7 +204,7 @@ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, /* Matte pass. */ if (kernel_shadow_catcher_is_matte_path(path_flag)) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive * sampling is based on how noisy the combined pass is as if there were no catchers in the * scene. */ @@ -206,7 +212,7 @@ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, /* Shadow catcher pass. */ if (kernel_shadow_catcher_is_object_pass(path_flag)) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher, contribution); return true; } @@ -215,7 +221,7 @@ ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, const uint32_t path_flag, - const float3 contribution, + const Spectrum contribution, const float transparent, ccl_global float *ccl_restrict buffer) { @@ -232,9 +238,11 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, /* Matte pass. */ if (kernel_shadow_catcher_is_matte_path(path_flag)) { + const float3 contribution_rgb = spectrum_to_rgb(contribution); + kernel_write_pass_float4( buffer + kernel_data.film.pass_shadow_catcher_matte, - make_float4(contribution.x, contribution.y, contribution.z, transparent)); + make_float4(contribution_rgb.x, contribution_rgb.y, contribution_rgb.z, transparent)); /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive * sampling is based on how noisy the combined pass is as if there were no catchers in the * scene. */ @@ -245,7 +253,7 @@ ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, /* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the * calculation and the alpha channel of the pass contains numbers of samples contributed to a * pixel of the pass. */ - kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_shadow_catcher, contribution); return true; } @@ -279,7 +287,7 @@ ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, const uint32_t path_flag, const int sample, - const float3 contribution, + const Spectrum contribution, ccl_global float *ccl_restrict buffer) { #ifdef __SHADOW_CATCHER__ @@ -289,7 +297,7 @@ ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, #endif if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_combined, contribution); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_combined, contribution); } kernel_accum_adaptive_buffer(kg, sample, contribution, buffer); @@ -299,7 +307,7 @@ ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, const uint32_t path_flag, const int sample, - const float3 contribution, + const Spectrum contribution, const float transparent, ccl_global float *ccl_restrict buffer) @@ -311,9 +319,11 @@ ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, #endif if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { + const float3 contribution_rgb = spectrum_to_rgb(contribution); + kernel_write_pass_float4( buffer + kernel_data.film.pass_combined, - make_float4(contribution.x, contribution.y, contribution.z, transparent)); + make_float4(contribution_rgb.x, contribution_rgb.y, contribution_rgb.z, transparent)); } kernel_accum_adaptive_buffer(kg, sample, contribution, buffer); @@ -323,7 +333,7 @@ ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, ccl_device_inline void kernel_accum_emission_or_background_pass( KernelGlobals kg, ConstIntegratorState state, - float3 contribution, + Spectrum contribution, ccl_global float *ccl_restrict buffer, const int pass, const int lightgroup = LIGHTGROUP_NONE) @@ -340,17 +350,18 @@ ccl_device_inline void kernel_accum_emission_or_background_pass( # ifdef __DENOISING_FEATURES__ if (path_flag & PATH_RAY_DENOISING_FEATURES) { if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { - const float3 denoising_feature_throughput = INTEGRATOR_STATE( + const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( state, path, denoising_feature_throughput); - const float3 denoising_albedo = denoising_feature_throughput * contribution; - kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + const Spectrum denoising_albedo = denoising_feature_throughput * contribution; + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, + denoising_albedo); } } # endif /* __DENOISING_FEATURES__ */ if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup, - contribution); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup, + contribution); } if (!(path_flag & PATH_RAY_ANY_PASS)) { @@ -366,15 +377,15 @@ ccl_device_inline void kernel_accum_emission_or_background_pass( if (path_flag & PATH_RAY_SURFACE_PASS) { /* Indirectly visible through reflection. */ - const float3 diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight); - const float3 glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight); + const Spectrum diffuse_weight = INTEGRATOR_STATE(state, path, pass_diffuse_weight); + const Spectrum glossy_weight = INTEGRATOR_STATE(state, path, pass_glossy_weight); /* Glossy */ const int glossy_pass_offset = ((INTEGRATOR_STATE(state, path, bounce) == 1) ? kernel_data.film.pass_glossy_direct : kernel_data.film.pass_glossy_indirect); if (glossy_pass_offset != PASS_UNUSED) { - kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_weight * contribution); + kernel_write_pass_spectrum(buffer + glossy_pass_offset, glossy_weight * contribution); } /* Transmission */ @@ -385,9 +396,9 @@ ccl_device_inline void kernel_accum_emission_or_background_pass( if (transmission_pass_offset != PASS_UNUSED) { /* Transmission is what remains if not diffuse and glossy, not stored explicitly to save * GPU memory. */ - const float3 transmission_weight = one_float3() - diffuse_weight - glossy_weight; - kernel_write_pass_float3(buffer + transmission_pass_offset, - transmission_weight * contribution); + const Spectrum transmission_weight = one_spectrum() - diffuse_weight - glossy_weight; + kernel_write_pass_spectrum(buffer + transmission_pass_offset, + transmission_weight * contribution); } /* Reconstruct diffuse subset of throughput. */ @@ -408,7 +419,7 @@ ccl_device_inline void kernel_accum_emission_or_background_pass( /* Single write call for GPU coherence. */ if (pass_offset != PASS_UNUSED) { - kernel_write_pass_float3(buffer + pass_offset, contribution); + kernel_write_pass_spectrum(buffer + pass_offset, contribution); } #endif /* __PASSES__ */ } @@ -419,7 +430,7 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, ccl_global float *ccl_restrict render_buffer) { /* The throughput for shadow paths already contains the light shader evaluation. */ - float3 contribution = INTEGRATOR_STATE(state, shadow_path, throughput); + Spectrum contribution = INTEGRATOR_STATE(state, shadow_path, throughput); kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, shadow_path, bounce)); const uint32_t render_pixel_index = INTEGRATOR_STATE(state, shadow_path, render_pixel_index); @@ -433,10 +444,10 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, /* Ambient occlusion. */ if (path_flag & PATH_RAY_SHADOW_FOR_AO) { if ((kernel_data.kernel_features & KERNEL_FEATURE_AO_PASS) && (path_flag & PATH_RAY_CAMERA)) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, contribution); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_ao, contribution); } if (kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) { - const float3 ao_weight = INTEGRATOR_STATE(state, shadow_path, unshadowed_throughput); + const Spectrum ao_weight = INTEGRATOR_STATE(state, shadow_path, unshadowed_throughput); kernel_accum_combined_pass(kg, path_flag, sample, contribution * ao_weight, buffer); } return; @@ -458,8 +469,8 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, /* Write lightgroup pass. LIGHTGROUP_NONE is ~0 so decode from unsigned to signed */ const int lightgroup = (int)(INTEGRATOR_STATE(state, shadow_path, lightgroup)) - 1; if (lightgroup != LIGHTGROUP_NONE && kernel_data.film.pass_lightgroup != PASS_UNUSED) { - kernel_write_pass_float3(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup, - contribution); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_lightgroup + 3 * lightgroup, + contribution); } if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { @@ -467,15 +478,15 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, if (path_flag & PATH_RAY_SURFACE_PASS) { /* Indirectly visible through reflection. */ - const float3 diffuse_weight = INTEGRATOR_STATE(state, shadow_path, pass_diffuse_weight); - const float3 glossy_weight = INTEGRATOR_STATE(state, shadow_path, pass_glossy_weight); + const Spectrum diffuse_weight = INTEGRATOR_STATE(state, shadow_path, pass_diffuse_weight); + const Spectrum glossy_weight = INTEGRATOR_STATE(state, shadow_path, pass_glossy_weight); /* Glossy */ const int glossy_pass_offset = ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? kernel_data.film.pass_glossy_direct : kernel_data.film.pass_glossy_indirect); if (glossy_pass_offset != PASS_UNUSED) { - kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_weight * contribution); + kernel_write_pass_spectrum(buffer + glossy_pass_offset, glossy_weight * contribution); } /* Transmission */ @@ -486,9 +497,9 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, if (transmission_pass_offset != PASS_UNUSED) { /* Transmission is what remains if not diffuse and glossy, not stored explicitly to save * GPU memory. */ - const float3 transmission_weight = one_float3() - diffuse_weight - glossy_weight; - kernel_write_pass_float3(buffer + transmission_pass_offset, - transmission_weight * contribution); + const Spectrum transmission_weight = one_spectrum() - diffuse_weight - glossy_weight; + kernel_write_pass_spectrum(buffer + transmission_pass_offset, + transmission_weight * contribution); } /* Reconstruct diffuse subset of throughput. */ @@ -508,19 +519,19 @@ ccl_device_inline void kernel_accum_light(KernelGlobals kg, /* Single write call for GPU coherence. */ if (pass_offset != PASS_UNUSED) { - kernel_write_pass_float3(buffer + pass_offset, contribution); + kernel_write_pass_spectrum(buffer + pass_offset, contribution); } } /* Write shadow pass. */ if (kernel_data.film.pass_shadow != PASS_UNUSED && (path_flag & PATH_RAY_SHADOW_FOR_LIGHT) && (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { - const float3 unshadowed_throughput = INTEGRATOR_STATE( + const Spectrum unshadowed_throughput = INTEGRATOR_STATE( state, shadow_path, unshadowed_throughput); - const float3 shadowed_throughput = INTEGRATOR_STATE(state, shadow_path, throughput); - const float3 shadow = safe_divide(shadowed_throughput, unshadowed_throughput) * - kernel_data.film.pass_shadow_scale; - kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow, shadow); + const Spectrum shadowed_throughput = INTEGRATOR_STATE(state, shadow_path, throughput); + const Spectrum shadow = safe_divide(shadowed_throughput, unshadowed_throughput) * + kernel_data.film.pass_shadow_scale; + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_shadow, shadow); } } #endif @@ -560,12 +571,12 @@ ccl_device_inline void kernel_accum_holdout(KernelGlobals kg, * Includes transparency, matching kernel_accum_transparent. */ ccl_device_inline void kernel_accum_background(KernelGlobals kg, ConstIntegratorState state, - const float3 L, + const Spectrum L, const float transparent, const bool is_transparent_background_ray, ccl_global float *ccl_restrict render_buffer) { - float3 contribution = float3(INTEGRATOR_STATE(state, path, throughput)) * L; + Spectrum contribution = INTEGRATOR_STATE(state, path, throughput) * L; kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); @@ -590,11 +601,11 @@ ccl_device_inline void kernel_accum_background(KernelGlobals kg, /* Write emission to render buffer. */ ccl_device_inline void kernel_accum_emission(KernelGlobals kg, ConstIntegratorState state, - const float3 L, + const Spectrum L, ccl_global float *ccl_restrict render_buffer, const int lightgroup = LIGHTGROUP_NONE) { - float3 contribution = L; + Spectrum contribution = L; kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); diff --git a/intern/cycles/kernel/film/passes.h b/intern/cycles/kernel/film/passes.h index 1f5cf2048f1..bea23411000 100644 --- a/intern/cycles/kernel/film/passes.h +++ b/intern/cycles/kernel/film/passes.h @@ -40,7 +40,7 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); if (kernel_data.film.pass_denoising_depth != PASS_UNUSED) { - const float3 denoising_feature_throughput = INTEGRATOR_STATE( + const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( state, path, denoising_feature_throughput); const float denoising_depth = ensure_finite(average(denoising_feature_throughput) * sd->ray_length); @@ -48,8 +48,8 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( } float3 normal = zero_float3(); - float3 diffuse_albedo = zero_float3(); - float3 specular_albedo = zero_float3(); + Spectrum diffuse_albedo = zero_spectrum(); + Spectrum specular_albedo = zero_spectrum(); float sum_weight = 0.0f, sum_nonspecular_weight = 0.0f; for (int i = 0; i < sd->num_closure; i++) { @@ -63,7 +63,7 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( normal += sc->N * sc->sample_weight; sum_weight += sc->sample_weight; - float3 closure_albedo = sc->weight; + Spectrum closure_albedo = sc->weight; /* Closures that include a Fresnel term typically have weights close to 1 even though their * actual contribution is significantly lower. * To account for this, we scale their weight by the average fresnel factor (the same is also @@ -113,10 +113,12 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( } if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { - const float3 denoising_feature_throughput = INTEGRATOR_STATE( + const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( state, path, denoising_feature_throughput); - const float3 denoising_albedo = ensure_finite(denoising_feature_throughput * diffuse_albedo); - kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + const Spectrum denoising_albedo = ensure_finite(denoising_feature_throughput * + diffuse_albedo); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, + denoising_albedo); } INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; @@ -128,13 +130,13 @@ ccl_device_forceinline void kernel_write_denoising_features_surface( ccl_device_forceinline void kernel_write_denoising_features_volume(KernelGlobals kg, IntegratorState state, - const float3 albedo, + const Spectrum albedo, const bool scatter, ccl_global float *ccl_restrict render_buffer) { ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); - const float3 denoising_feature_throughput = INTEGRATOR_STATE( + const Spectrum denoising_feature_throughput = INTEGRATOR_STATE( state, path, denoising_feature_throughput); if (scatter && kernel_data.film.pass_denoising_normal != PASS_UNUSED) { @@ -148,8 +150,8 @@ ccl_device_forceinline void kernel_write_denoising_features_volume(KernelGlobals if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { /* Write albedo. */ - const float3 denoising_albedo = ensure_finite(denoising_feature_throughput * albedo); - kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + const Spectrum denoising_albedo = ensure_finite(denoising_feature_throughput * albedo); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); } } #endif /* __DENOISING_FEATURES__ */ @@ -228,7 +230,7 @@ ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, } if (kernel_data.film.cryptomatte_passes) { - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); const float matte_weight = average(throughput) * (1.0f - average(shader_bsdf_transparency(kg, sd))); if (matte_weight > 0.0f) { @@ -252,19 +254,19 @@ ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, } if (flag & PASSMASK(DIFFUSE_COLOR)) { - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_color, - shader_bsdf_diffuse(kg, sd) * throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_diffuse_color, + shader_bsdf_diffuse(kg, sd) * throughput); } if (flag & PASSMASK(GLOSSY_COLOR)) { - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_color, - shader_bsdf_glossy(kg, sd) * throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_glossy_color, + shader_bsdf_glossy(kg, sd) * throughput); } if (flag & PASSMASK(TRANSMISSION_COLOR)) { - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); - kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_color, - shader_bsdf_transmission(kg, sd) * throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_write_pass_spectrum(buffer + kernel_data.film.pass_transmission_color, + shader_bsdf_transmission(kg, sd) * throughput); } if (flag & PASSMASK(MIST)) { /* Bring depth into 0..1 range. */ @@ -287,8 +289,8 @@ ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, mist = powf(mist, mist_falloff); /* Modulate by transparency */ - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); - const float3 alpha = shader_bsdf_alpha(kg, sd); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum alpha = shader_bsdf_alpha(kg, sd); const float mist_output = (1.0f - mist) * average(throughput * alpha); /* Note that the final value in the render buffer we want is 1 - mist_output, diff --git a/intern/cycles/kernel/film/write_passes.h b/intern/cycles/kernel/film/write_passes.h index 9148d73518f..c78116cedc6 100644 --- a/intern/cycles/kernel/film/write_passes.h +++ b/intern/cycles/kernel/film/write_passes.h @@ -3,6 +3,8 @@ #pragma once +#include "kernel/util/color.h" + #ifdef __KERNEL_GPU__ # define __ATOMIC_PASS_WRITE__ #endif @@ -36,6 +38,12 @@ ccl_device_inline void kernel_write_pass_float3(ccl_global float *ccl_restrict b #endif } +ccl_device_inline void kernel_write_pass_spectrum(ccl_global float *ccl_restrict buffer, + Spectrum value) +{ + kernel_write_pass_float3(buffer, spectrum_to_rgb(value)); +} + ccl_device_inline void kernel_write_pass_float4(ccl_global float *ccl_restrict buffer, float4 value) { diff --git a/intern/cycles/kernel/geom/shader_data.h b/intern/cycles/kernel/geom/shader_data.h index 5af89b45f20..028c03ace1d 100644 --- a/intern/cycles/kernel/geom/shader_data.h +++ b/intern/cycles/kernel/geom/shader_data.h @@ -123,9 +123,9 @@ ccl_device_inline void shader_setup_from_ray(KernelGlobals kg, #ifdef __RAY_DIFFERENTIALS__ /* differentials */ - differential_transfer_compact(&sd->dP, ray->dP, ray->D, ray->dD, sd->Ng, sd->ray_length); - differential_incoming_compact(&sd->dI, ray->D, ray->dD); - differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng); + sd->dP = differential_transfer_compact(ray->dP, ray->D, ray->dD, sd->ray_length); + sd->dI = differential_incoming_compact(ray->dD); + differential_dudv_compact(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng); #endif } @@ -240,8 +240,8 @@ ccl_device_inline void shader_setup_from_sample(KernelGlobals kg, #ifdef __RAY_DIFFERENTIALS__ /* no ray differentials here yet */ - sd->dP = differential3_zero(); - sd->dI = differential3_zero(); + sd->dP = differential_zero_compact(); + sd->dI = differential_zero_compact(); sd->du = differential_zero(); sd->dv = differential_zero(); #endif @@ -348,8 +348,8 @@ ccl_device void shader_setup_from_curve(KernelGlobals kg, /* No ray differentials currently. */ #ifdef __RAY_DIFFERENTIALS__ - sd->dP = differential3_zero(); - sd->dI = differential3_zero(); + sd->dP = differential_zero_compact(); + sd->dI = differential_zero_compact(); sd->du = differential_zero(); sd->dv = differential_zero(); #endif @@ -391,8 +391,8 @@ ccl_device_inline void shader_setup_from_background(KernelGlobals kg, #ifdef __RAY_DIFFERENTIALS__ /* differentials */ - sd->dP = differential3_zero(); /* TODO: ray->dP */ - differential_incoming(&sd->dI, sd->dP); + sd->dP = differential_zero_compact(); /* TODO: ray->dP */ + sd->dI = differential_zero_compact(); sd->du = differential_zero(); sd->dv = differential_zero(); #endif @@ -433,8 +433,8 @@ ccl_device_inline void shader_setup_from_volume(KernelGlobals kg, # ifdef __RAY_DIFFERENTIALS__ /* differentials */ - sd->dP = differential3_zero(); /* TODO ray->dD */ - differential_incoming(&sd->dI, sd->dP); + sd->dP = differential_zero_compact(); /* TODO ray->dD */ + sd->dI = differential_zero_compact(); sd->du = differential_zero(); sd->dv = differential_zero(); # endif diff --git a/intern/cycles/kernel/integrator/mnee.h b/intern/cycles/kernel/integrator/mnee.h index aa72b93c9d2..c95f1557f04 100644 --- a/intern/cycles/kernel/integrator/mnee.h +++ b/intern/cycles/kernel/integrator/mnee.h @@ -392,7 +392,7 @@ ccl_device_forceinline bool mnee_compute_constraint_derivatives( /* Invert (block) constraint derivative matrix and solve linear system so we can map dh back to dx: * dh / dx = A * dx = inverse(A) x dh - * to use for specular specular manifold walk + * to use for specular manifold walk * (See for example http://faculty.washington.edu/finlayso/ebook/algebraic/advanced/LUtri.htm * for block tridiagonal matrix based linear system solve) */ ccl_device_forceinline bool mnee_solve_matrix_h_to_x(int vertex_count, @@ -634,9 +634,9 @@ mnee_sample_bsdf_dh(ClosureType type, float alpha_x, float alpha_y, float sample * We assume here that the pdf (in half-vector measure) is the same as * the one calculation when sampling the microfacet normals from the * specular chain above: this allows us to simplify the bsdf weight */ -ccl_device_forceinline float3 mnee_eval_bsdf_contribution(ccl_private ShaderClosure *closure, - float3 wi, - float3 wo) +ccl_device_forceinline Spectrum mnee_eval_bsdf_contribution(ccl_private ShaderClosure *closure, + float3 wi, + float3 wo) { ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)closure; @@ -835,7 +835,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, 1; INTEGRATOR_STATE_WRITE(state, path, bounce) = bounce + vertex_count; - float3 light_eval = light_sample_shader_eval(kg, state, sd_mnee, ls, sd->time); + Spectrum light_eval = light_sample_shader_eval(kg, state, sd_mnee, ls, sd->time); bsdf_eval_mul(throughput, light_eval / ls->pdf); /* Generalized geometry term. */ @@ -924,7 +924,7 @@ ccl_device_forceinline bool mnee_path_contribution(KernelGlobals kg, /* Evaluate product term inside eq.6 at solution interface. vi * divided by corresponding sampled pdf: * fr(vi)_do / pdf_dh(vi) x |do/dh| x |n.wo / n.h| */ - float3 bsdf_contribution = mnee_eval_bsdf_contribution(v.bsdf, wi, wo); + Spectrum bsdf_contribution = mnee_eval_bsdf_contribution(v.bsdf, wi, wo); bsdf_eval_mul(throughput, bsdf_contribution); } diff --git a/intern/cycles/kernel/integrator/path_state.h b/intern/cycles/kernel/integrator/path_state.h index b09bc117d78..5ec94b934ca 100644 --- a/intern/cycles/kernel/integrator/path_state.h +++ b/intern/cycles/kernel/integrator/path_state.h @@ -54,7 +54,7 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg, INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = 0.0f; INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = FLT_MAX; INTEGRATOR_STATE_WRITE(state, path, continuation_probability) = 1.0f; - INTEGRATOR_STATE_WRITE(state, path, throughput) = make_float3(1.0f, 1.0f, 1.0f); + INTEGRATOR_STATE_WRITE(state, path, throughput) = one_spectrum(); #ifdef __MNEE__ INTEGRATOR_STATE_WRITE(state, path, mnee) = 0; @@ -74,7 +74,7 @@ ccl_device_inline void path_state_init_integrator(KernelGlobals kg, #ifdef __DENOISING_FEATURES__ if (kernel_data.kernel_features & KERNEL_FEATURE_DENOISING) { INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_DENOISING_FEATURES; - INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) = one_float3(); + INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) = one_spectrum(); } #endif } diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h index a7edfffd175..57d060d58df 100644 --- a/intern/cycles/kernel/integrator/shade_background.h +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -10,9 +10,9 @@ CCL_NAMESPACE_BEGIN -ccl_device float3 integrator_eval_background_shader(KernelGlobals kg, - IntegratorState state, - ccl_global float *ccl_restrict render_buffer) +ccl_device Spectrum integrator_eval_background_shader(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) { #ifdef __BACKGROUND__ const int shader = kernel_data.background.surface_shader; @@ -26,11 +26,11 @@ ccl_device float3 integrator_eval_background_shader(KernelGlobals kg, ((shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || ((shader & SHADER_EXCLUDE_CAMERA) && (path_flag & PATH_RAY_CAMERA)) || ((shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) - return zero_float3(); + return zero_spectrum(); } /* Use fast constant background color if available. */ - float3 L = zero_float3(); + Spectrum L = zero_spectrum(); if (!shader_constant_emission_eval(kg, shader, &L)) { /* Evaluate background shader. */ @@ -73,7 +73,7 @@ ccl_device float3 integrator_eval_background_shader(KernelGlobals kg, return L; #else - return make_float3(0.8f, 0.8f, 0.8f); + return make_spectrum(0.8f); #endif } @@ -117,8 +117,8 @@ ccl_device_inline void integrate_background(KernelGlobals kg, #endif /* __MNEE__ */ /* Evaluate background shader. */ - float3 L = (eval_background) ? integrator_eval_background_shader(kg, state, render_buffer) : - zero_float3(); + Spectrum L = (eval_background) ? integrator_eval_background_shader(kg, state, render_buffer) : + zero_spectrum(); /* When using the ao bounces approximation, adjust background * shader intensity with ao factor. */ @@ -169,7 +169,7 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg, /* TODO: does aliasing like this break automatic SoA in CUDA? */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); + Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); if (is_zero(light_eval)) { return; } @@ -184,7 +184,7 @@ ccl_device_inline void integrate_distant_lights(KernelGlobals kg, } /* Write to render buffer. */ - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); kernel_accum_emission( kg, state, throughput * light_eval, render_buffer, kernel_data.background.lightgroup); } diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h index 910e3383f51..ac9d1415abb 100644 --- a/intern/cycles/kernel/integrator/shade_light.h +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -51,7 +51,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg, /* TODO: does aliasing like this break automatic SoA in CUDA? */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); + Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); if (is_zero(light_eval)) { return; } @@ -66,7 +66,7 @@ ccl_device_inline void integrate_light(KernelGlobals kg, } /* Write to render buffer. */ - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); kernel_accum_emission(kg, state, throughput * light_eval, render_buffer, ls.group); } diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h index 4b002a47bee..a52706a77f1 100644 --- a/intern/cycles/kernel/integrator/shade_shadow.h +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -15,9 +15,9 @@ ccl_device_inline bool shadow_intersections_has_remaining(const uint num_hits) } #ifdef __TRANSPARENT_SHADOWS__ -ccl_device_inline float3 integrate_transparent_surface_shadow(KernelGlobals kg, - IntegratorShadowState state, - const int hit) +ccl_device_inline Spectrum integrate_transparent_surface_shadow(KernelGlobals kg, + IntegratorShadowState state, + const int hit) { PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SURFACE); @@ -58,7 +58,7 @@ ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, IntegratorShadowState state, const int hit, const int num_recorded_hits, - ccl_private float3 *ccl_restrict + ccl_private Spectrum *ccl_restrict throughput) { PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_VOLUME); @@ -100,7 +100,7 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, if (hit < num_recorded_hits || !shadow_intersections_has_remaining(num_hits)) { # ifdef __VOLUME__ if (!integrator_state_shadow_volume_stack_is_empty(kg, state)) { - float3 throughput = INTEGRATOR_STATE(state, shadow_path, throughput); + Spectrum throughput = INTEGRATOR_STATE(state, shadow_path, throughput); integrate_transparent_volume_shadow(kg, state, hit, num_recorded_hits, &throughput); if (is_zero(throughput)) { return true; @@ -113,8 +113,8 @@ ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, /* Surface shaders. */ if (hit < num_recorded_hits) { - const float3 shadow = integrate_transparent_surface_shadow(kg, state, hit); - const float3 throughput = INTEGRATOR_STATE(state, shadow_path, throughput) * shadow; + const Spectrum shadow = integrate_transparent_surface_shadow(kg, state, hit); + const Spectrum throughput = INTEGRATOR_STATE(state, shadow_path, throughput) * shadow; if (is_zero(throughput)) { return true; } diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h index 19b8946e865..f42e2979b3b 100644 --- a/intern/cycles/kernel/integrator/shade_surface.h +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -44,7 +44,7 @@ ccl_device_forceinline float3 integrate_surface_ray_offset(KernelGlobals kg, /* Self intersection tests already account for the case where a ray hits the * same primitive. However precision issues can still cause neighboring * triangles to be hit. Here we test if the ray-triangle intersection with - * the same primitive would miss, implying that a neighbouring triangle would + * the same primitive would miss, implying that a neighboring triangle would * be hit instead. * * This relies on triangle intersection to be watertight, and the object inverse @@ -88,11 +88,11 @@ ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { - const float3 holdout_weight = shader_holdout_apply(kg, sd); - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum holdout_weight = shader_holdout_apply(kg, sd); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); const float transparent = average(holdout_weight * throughput); kernel_accum_holdout(kg, state, path_flag, transparent, render_buffer); - if (isequal(holdout_weight, one_float3())) { + if (isequal(holdout_weight, one_spectrum())) { return false; } } @@ -111,7 +111,7 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); /* Evaluate emissive closure. */ - float3 L = shader_emissive_eval(sd); + Spectrum L = shader_emissive_eval(sd); # ifdef __HAIR__ if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) && @@ -130,7 +130,7 @@ ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, L *= mis_weight; } - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); kernel_accum_emission( kg, state, throughput * L, render_buffer, object_lightgroup(kg, sd->object)); } @@ -207,7 +207,7 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, else # endif /* __MNEE__ */ { - const float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, sd->time); + const Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, sd->time); if (is_zero(light_eval)) { return; } @@ -261,11 +261,12 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, /* Copy state from main path to shadow path. */ uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; - const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * bsdf_eval_sum(&bsdf_eval); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput) * + bsdf_eval_sum(&bsdf_eval); if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { - packed_float3 pass_diffuse_weight; - packed_float3 pass_glossy_weight; + PackedSpectrum pass_diffuse_weight; + PackedSpectrum pass_glossy_weight; if (shadow_flag & PATH_RAY_ANY_PASS) { /* Indirect bounce, use weights from earlier surface or volume bounce. */ @@ -275,8 +276,8 @@ ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, else { /* Direct light, use BSDFs at this bounce. */ shadow_flag |= PATH_RAY_SURFACE_PASS; - pass_diffuse_weight = packed_float3(bsdf_eval_pass_diffuse_weight(&bsdf_eval)); - pass_glossy_weight = packed_float3(bsdf_eval_pass_glossy_weight(&bsdf_eval)); + pass_diffuse_weight = PackedSpectrum(bsdf_eval_pass_diffuse_weight(&bsdf_eval)); + pass_glossy_weight = PackedSpectrum(bsdf_eval_pass_glossy_weight(&bsdf_eval)); } INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight; @@ -361,11 +362,10 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( float bsdf_pdf; BsdfEval bsdf_eval ccl_optional_struct_init; float3 bsdf_omega_in ccl_optional_struct_init; - differential3 bsdf_domega_in ccl_optional_struct_init; int label; label = shader_bsdf_sample_closure( - kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf); + kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_pdf); if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) { return LABEL_NONE; @@ -384,12 +384,11 @@ ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX; #ifdef __RAY_DIFFERENTIALS__ INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); - INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in); #endif } /* Update throughput. */ - float3 throughput = INTEGRATOR_STATE(state, path, throughput); + Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); throughput *= bsdf_eval_sum(&bsdf_eval) / bsdf_pdf; INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput; @@ -461,7 +460,7 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); float3 ao_N; - const float3 ao_weight = shader_bsdf_ao( + const Spectrum ao_weight = shader_bsdf_ao( kg, sd, kernel_data.integrator.ao_additive_factor, &ao_N); float3 ao_D; @@ -504,7 +503,8 @@ ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag) | PATH_RAY_SHADOW_FOR_AO; - const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * shader_bsdf_alpha(kg, sd); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput) * + shader_bsdf_alpha(kg, sd); INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( state, path, render_pixel_index); diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h index 4aab097a7d8..599454c5cb2 100644 --- a/intern/cycles/kernel/integrator/shade_volume.h +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -29,13 +29,13 @@ typedef enum VolumeIntegrateEvent { typedef struct VolumeIntegrateResult { /* Throughput and offset for direct light scattering. */ bool direct_scatter; - float3 direct_throughput; + Spectrum direct_throughput; float direct_t; ShaderVolumePhases direct_phases; /* Throughput and offset for indirect light scattering. */ bool indirect_scatter; - float3 indirect_throughput; + Spectrum indirect_throughput; float indirect_t; ShaderVolumePhases indirect_phases; } VolumeIntegrateResult; @@ -52,16 +52,16 @@ typedef struct VolumeIntegrateResult { * sigma_t = sigma_a + sigma_s */ typedef struct VolumeShaderCoefficients { - float3 sigma_t; - float3 sigma_s; - float3 emission; + Spectrum sigma_t; + Spectrum sigma_s; + Spectrum emission; } VolumeShaderCoefficients; /* Evaluate shader to get extinction coefficient at P. */ ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg, IntegratorShadowState state, ccl_private ShaderData *ccl_restrict sd, - ccl_private float3 *ccl_restrict extinction) + ccl_private Spectrum *ccl_restrict extinction) { VOLUME_READ_LAMBDA(integrator_state_read_shadow_volume_stack(state, i)) shader_eval_volume<true>(kg, state, sd, PATH_RAY_SHADOW, volume_read_lambda_pass); @@ -89,9 +89,10 @@ ccl_device_inline bool volume_shader_sample(KernelGlobals kg, return false; } - coeff->sigma_s = zero_float3(); - coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : zero_float3(); - coeff->emission = (sd->flag & SD_EMISSION) ? sd->closure_emission_background : zero_float3(); + coeff->sigma_s = zero_spectrum(); + coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : + zero_spectrum(); + coeff->emission = (sd->flag & SD_EMISSION) ? sd->closure_emission_background : zero_spectrum(); if (sd->flag & SD_SCATTER) { for (int i = 0; i < sd->num_closure; i++) { @@ -162,9 +163,9 @@ ccl_device_forceinline void volume_step_init(KernelGlobals kg, ccl_device void volume_shadow_homogeneous(KernelGlobals kg, IntegratorState state, ccl_private Ray *ccl_restrict ray, ccl_private ShaderData *ccl_restrict sd, - ccl_global float3 *ccl_restrict throughput) + ccl_global Spectrum *ccl_restrict throughput) { - float3 sigma_t = zero_float3(); + Spectrum sigma_t = zero_spectrum(); if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { *throughput *= volume_color_transmittance(sigma_t, ray->tmax - ray->tmin); @@ -178,14 +179,14 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, IntegratorShadowState state, ccl_private Ray *ccl_restrict ray, ccl_private ShaderData *ccl_restrict sd, - ccl_private float3 *ccl_restrict throughput, + ccl_private Spectrum *ccl_restrict throughput, const float object_step_size) { /* Load random number state. */ RNGState rng_state; shadow_path_state_rng_load(state, &rng_state); - float3 tp = *throughput; + Spectrum tp = *throughput; /* Prepare for stepping. * For shadows we do not offset all segments, since the starting point is @@ -207,7 +208,7 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, /* compute extinction at the start */ float t = ray->tmin; - float3 sum = zero_float3(); + Spectrum sum = zero_spectrum(); for (int i = 0; i < max_steps; i++) { /* advance to new position */ @@ -215,7 +216,7 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, float dt = new_t - t; float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); - float3 sigma_t = zero_float3(); + Spectrum sigma_t = zero_spectrum(); /* compute attenuation over segment */ sd->P = new_P; @@ -228,8 +229,7 @@ ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, tp = *throughput * exp(sum); /* stop if nearly all light is blocked */ - if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON && - tp.z < VOLUME_THROUGHPUT_EPSILON) + if (reduce_max(tp) < VOLUME_THROUGHPUT_EPSILON) break; } } @@ -334,22 +334,22 @@ ccl_device float volume_equiangular_cdf(ccl_private const Ray *ccl_restrict ray, /* Distance sampling */ ccl_device float volume_distance_sample(float max_t, - float3 sigma_t, + Spectrum sigma_t, int channel, float xi, - ccl_private float3 *transmittance, - ccl_private float3 *pdf) + ccl_private Spectrum *transmittance, + ccl_private Spectrum *pdf) { /* xi is [0, 1[ so log(0) should never happen, division by zero is * avoided because sample_sigma_t > 0 when SD_SCATTER is set */ float sample_sigma_t = volume_channel_get(sigma_t, channel); - float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); + Spectrum full_transmittance = volume_color_transmittance(sigma_t, max_t); float sample_transmittance = volume_channel_get(full_transmittance, channel); float sample_t = min(max_t, -logf(1.0f - xi * (1.0f - sample_transmittance)) / sample_sigma_t); *transmittance = volume_color_transmittance(sigma_t, sample_t); - *pdf = safe_divide_color(sigma_t * *transmittance, one_float3() - full_transmittance); + *pdf = safe_divide_color(sigma_t * *transmittance, one_spectrum() - full_transmittance); /* todo: optimization: when taken together with hit/miss decision, * the full_transmittance cancels out drops out and xi does not @@ -358,33 +358,36 @@ ccl_device float volume_distance_sample(float max_t, return sample_t; } -ccl_device float3 volume_distance_pdf(float max_t, float3 sigma_t, float sample_t) +ccl_device Spectrum volume_distance_pdf(float max_t, Spectrum sigma_t, float sample_t) { - float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); - float3 transmittance = volume_color_transmittance(sigma_t, sample_t); + Spectrum full_transmittance = volume_color_transmittance(sigma_t, max_t); + Spectrum transmittance = volume_color_transmittance(sigma_t, sample_t); - return safe_divide_color(sigma_t * transmittance, one_float3() - full_transmittance); + return safe_divide_color(sigma_t * transmittance, one_spectrum() - full_transmittance); } /* Emission */ -ccl_device float3 volume_emission_integrate(ccl_private VolumeShaderCoefficients *coeff, - int closure_flag, - float3 transmittance, - float t) +ccl_device Spectrum volume_emission_integrate(ccl_private VolumeShaderCoefficients *coeff, + int closure_flag, + Spectrum transmittance, + float t) { /* integral E * exp(-sigma_t * t) from 0 to t = E * (1 - exp(-sigma_t * t))/sigma_t * this goes to E * t as sigma_t goes to zero * * todo: we should use an epsilon to avoid precision issues near zero sigma_t */ - float3 emission = coeff->emission; + Spectrum emission = coeff->emission; if (closure_flag & SD_EXTINCTION) { - float3 sigma_t = coeff->sigma_t; + Spectrum sigma_t = coeff->sigma_t; - emission.x *= (sigma_t.x > 0.0f) ? (1.0f - transmittance.x) / sigma_t.x : t; - emission.y *= (sigma_t.y > 0.0f) ? (1.0f - transmittance.y) / sigma_t.y : t; - emission.z *= (sigma_t.z > 0.0f) ? (1.0f - transmittance.z) / sigma_t.z : t; + FOREACH_SPECTRUM_CHANNEL (i) { + GET_SPECTRUM_CHANNEL(emission, i) *= (GET_SPECTRUM_CHANNEL(sigma_t, i) > 0.0f) ? + (1.0f - GET_SPECTRUM_CHANNEL(transmittance, i)) / + GET_SPECTRUM_CHANNEL(sigma_t, i) : + t; + } } else emission *= t; @@ -419,14 +422,14 @@ ccl_device_forceinline void volume_integrate_step_scattering( ccl_private const Ray *ray, const float3 equiangular_light_P, ccl_private const VolumeShaderCoefficients &ccl_restrict coeff, - const float3 transmittance, + const Spectrum transmittance, ccl_private VolumeIntegrateState &ccl_restrict vstate, ccl_private VolumeIntegrateResult &ccl_restrict result) { /* Pick random color channel, we use the Veach one-sample * model with balance heuristic for the channels. */ - const float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); - float3 channel_pdf; + const Spectrum albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); + Spectrum channel_pdf; const int channel = volume_sample_channel( albedo, result.indirect_throughput, vstate.rphase, &channel_pdf); @@ -435,7 +438,7 @@ ccl_device_forceinline void volume_integrate_step_scattering( if (result.direct_t >= vstate.tmin && result.direct_t <= vstate.tmax && vstate.equiangular_pdf > VOLUME_SAMPLE_PDF_CUTOFF) { const float new_dt = result.direct_t - vstate.tmin; - const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); + const Spectrum new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); result.direct_scatter = true; result.direct_throughput *= coeff.sigma_s * new_transmittance / vstate.equiangular_pdf; @@ -467,7 +470,7 @@ ccl_device_forceinline void volume_integrate_step_scattering( const float new_t = vstate.tmin + new_dt; /* transmittance and pdf */ - const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); + const Spectrum new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); const float distance_pdf = dot(channel_pdf, coeff.sigma_t * new_transmittance); if (vstate.distance_pdf * distance_pdf > VOLUME_SAMPLE_PDF_CUTOFF) { @@ -566,7 +569,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( vstate.distance_pdf = 1.0f; /* Initialize volume integration result. */ - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); result.direct_throughput = throughput; result.indirect_throughput = throughput; @@ -579,9 +582,9 @@ ccl_device_forceinline void volume_integrate_heterogeneous( # ifdef __DENOISING_FEATURES__ const bool write_denoising_features = (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_DENOISING_FEATURES); - float3 accum_albedo = zero_float3(); + Spectrum accum_albedo = zero_spectrum(); # endif - float3 accum_emission = zero_float3(); + Spectrum accum_emission = zero_spectrum(); for (int i = 0; i < max_steps; i++) { /* Advance to new position */ @@ -596,16 +599,16 @@ ccl_device_forceinline void volume_integrate_heterogeneous( /* Evaluate transmittance over segment. */ const float dt = (vstate.tmax - vstate.tmin); - const float3 transmittance = (closure_flag & SD_EXTINCTION) ? - volume_color_transmittance(coeff.sigma_t, dt) : - one_float3(); + const Spectrum transmittance = (closure_flag & SD_EXTINCTION) ? + volume_color_transmittance(coeff.sigma_t, dt) : + one_spectrum(); /* Emission. */ if (closure_flag & SD_EMISSION) { /* Only write emission before indirect light scatter position, since we terminate * stepping at that point if we have already found a direct light scatter position. */ if (!result.indirect_scatter) { - const float3 emission = volume_emission_integrate( + const Spectrum emission = volume_emission_integrate( &coeff, closure_flag, transmittance, dt); accum_emission += result.indirect_throughput * emission; } @@ -616,8 +619,8 @@ ccl_device_forceinline void volume_integrate_heterogeneous( # ifdef __DENOISING_FEATURES__ /* Accumulate albedo for denoising features. */ if (write_denoising_features && (closure_flag & SD_SCATTER)) { - const float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); - accum_albedo += result.indirect_throughput * albedo * (one_float3() - transmittance); + const Spectrum albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); + accum_albedo += result.indirect_throughput * albedo * (one_spectrum() - transmittance); } # endif @@ -634,7 +637,7 @@ ccl_device_forceinline void volume_integrate_heterogeneous( /* Stop if nearly all light blocked. */ if (!result.indirect_scatter) { if (reduce_max(result.indirect_throughput) < VOLUME_THROUGHPUT_EPSILON) { - result.indirect_throughput = zero_float3(); + result.indirect_throughput = zero_spectrum(); break; } } @@ -715,7 +718,7 @@ ccl_device_forceinline void integrate_volume_direct_light( ccl_private const RNGState *ccl_restrict rng_state, const float3 P, ccl_private const ShaderVolumePhases *ccl_restrict phases, - ccl_private const float3 throughput, + ccl_private const Spectrum throughput, ccl_private LightSample *ccl_restrict ls) { PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT); @@ -753,7 +756,7 @@ ccl_device_forceinline void integrate_volume_direct_light( * non-constant light sources. */ ShaderDataTinyStorage emission_sd_storage; ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - const float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, ls, sd->time); + const Spectrum light_eval = light_sample_shader_eval(kg, state, emission_sd, ls, sd->time); if (is_zero(light_eval)) { return; } @@ -796,11 +799,11 @@ ccl_device_forceinline void integrate_volume_direct_light( const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; - const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval); + const Spectrum throughput_phase = throughput * bsdf_eval_sum(&phase_eval); if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { - packed_float3 pass_diffuse_weight; - packed_float3 pass_glossy_weight; + PackedSpectrum pass_diffuse_weight; + PackedSpectrum pass_glossy_weight; if (shadow_flag & PATH_RAY_ANY_PASS) { /* Indirect bounce, use weights from earlier surface or volume bounce. */ @@ -810,8 +813,8 @@ ccl_device_forceinline void integrate_volume_direct_light( else { /* Direct light, no diffuse/glossy distinction needed for volumes. */ shadow_flag |= PATH_RAY_VOLUME_PASS; - pass_diffuse_weight = packed_float3(one_float3()); - pass_glossy_weight = packed_float3(zero_float3()); + pass_diffuse_weight = one_spectrum(); + pass_glossy_weight = zero_spectrum(); } INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, pass_diffuse_weight) = pass_diffuse_weight; @@ -868,17 +871,9 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( float phase_pdf; BsdfEval phase_eval ccl_optional_struct_init; float3 phase_omega_in ccl_optional_struct_init; - differential3 phase_domega_in ccl_optional_struct_init; - - const int label = shader_volume_phase_sample(kg, - sd, - phases, - phase_u, - phase_v, - &phase_eval, - &phase_omega_in, - &phase_domega_in, - &phase_pdf); + + const int label = shader_volume_phase_sample( + kg, sd, phases, phase_u, phase_v, &phase_eval, &phase_omega_in, &phase_pdf); if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) { return false; @@ -891,20 +886,19 @@ ccl_device_forceinline bool integrate_volume_phase_scatter( INTEGRATOR_STATE_WRITE(state, ray, tmax) = FLT_MAX; # ifdef __RAY_DIFFERENTIALS__ INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); - INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in); # endif // Save memory by storing last hit prim and object in isect INTEGRATOR_STATE_WRITE(state, isect, prim) = sd->prim; INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object; /* Update throughput. */ - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); - const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf; + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf; INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput_phase; if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { - INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_float3(); - INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = zero_float3(); + INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_spectrum(); + INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = zero_spectrum(); } /* Update path state */ diff --git a/intern/cycles/kernel/integrator/shader_eval.h b/intern/cycles/kernel/integrator/shader_eval.h index ed4d973e864..e6b0d0a6466 100644 --- a/intern/cycles/kernel/integrator/shader_eval.h +++ b/intern/cycles/kernel/integrator/shader_eval.h @@ -98,7 +98,7 @@ ccl_device_inline void shader_prepare_surface_closures(KernelGlobals kg, /* Filter out closures. */ if (kernel_data.integrator.filter_closures) { if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_EMISSION) { - sd->closure_emission_background = zero_float3(); + sd->closure_emission_background = zero_spectrum(); } if (kernel_data.integrator.filter_closures & FILTER_CLOSURE_DIRECT_LIGHT) { @@ -231,7 +231,7 @@ ccl_device_inline float _shader_bsdf_multi_eval(KernelGlobals kg, if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { if (CLOSURE_IS_BSDF(sc->type) && !_shader_bsdf_exclude(sc->type, light_shader_flags)) { float bsdf_pdf = 0.0f; - float3 eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf); + Spectrum eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf); if (bsdf_pdf != 0.0f) { bsdf_eval_accum(result_eval, sc->type, eval * sc->weight); @@ -259,7 +259,7 @@ ccl_device_inline ccl_private BsdfEval *bsdf_eval, const uint light_shader_flags) { - bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_float3()); + bsdf_eval_init(bsdf_eval, CLOSURE_NONE_ID, zero_spectrum()); return _shader_bsdf_multi_eval( kg, sd, omega_in, is_transmission, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags); @@ -309,11 +309,11 @@ ccl_device_inline ccl_private const ShaderClosure *shader_bsdf_bssrdf_pick( } /* Return weight for picked BSSRDF. */ -ccl_device_inline float3 +ccl_device_inline Spectrum shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict sd, ccl_private const ShaderClosure *ccl_restrict bssrdf_sc) { - float3 weight = bssrdf_sc->weight; + Spectrum weight = bssrdf_sc->weight; if (sd->num_closure > 1) { float sum = 0.0f; @@ -339,17 +339,16 @@ ccl_device int shader_bsdf_sample_closure(KernelGlobals kg, float randv, ccl_private BsdfEval *bsdf_eval, ccl_private float3 *omega_in, - ccl_private differential3 *domega_in, ccl_private float *pdf) { /* BSSRDF should already have been handled elsewhere. */ kernel_assert(CLOSURE_IS_BSDF(sc->type)); int label; - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); *pdf = 0.0f; - label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); + label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, pdf); if (*pdf != 0.0f) { bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight); @@ -385,16 +384,16 @@ ccl_device float shader_bsdf_average_roughness(ccl_private const ShaderData *sd) return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f; } -ccl_device float3 shader_bsdf_transparency(KernelGlobals kg, ccl_private const ShaderData *sd) +ccl_device Spectrum shader_bsdf_transparency(KernelGlobals kg, ccl_private const ShaderData *sd) { if (sd->flag & SD_HAS_ONLY_VOLUME) { - return one_float3(); + return one_spectrum(); } else if (sd->flag & SD_TRANSPARENT) { return sd->closure_transparent_extinction; } else { - return zero_float3(); + return zero_spectrum(); } } @@ -406,7 +405,7 @@ ccl_device void shader_bsdf_disable_transparency(KernelGlobals kg, ccl_private S if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) { sc->sample_weight = 0.0f; - sc->weight = zero_float3(); + sc->weight = zero_spectrum(); } } @@ -414,19 +413,18 @@ ccl_device void shader_bsdf_disable_transparency(KernelGlobals kg, ccl_private S } } -ccl_device float3 shader_bsdf_alpha(KernelGlobals kg, ccl_private const ShaderData *sd) +ccl_device Spectrum shader_bsdf_alpha(KernelGlobals kg, ccl_private const ShaderData *sd) { - float3 alpha = one_float3() - shader_bsdf_transparency(kg, sd); + Spectrum alpha = one_spectrum() - shader_bsdf_transparency(kg, sd); - alpha = max(alpha, zero_float3()); - alpha = min(alpha, one_float3()); + alpha = saturate(alpha); return alpha; } -ccl_device float3 shader_bsdf_diffuse(KernelGlobals kg, ccl_private const ShaderData *sd) +ccl_device Spectrum shader_bsdf_diffuse(KernelGlobals kg, ccl_private const ShaderData *sd) { - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); for (int i = 0; i < sd->num_closure; i++) { ccl_private const ShaderClosure *sc = &sd->closure[i]; @@ -438,9 +436,9 @@ ccl_device float3 shader_bsdf_diffuse(KernelGlobals kg, ccl_private const Shader return eval; } -ccl_device float3 shader_bsdf_glossy(KernelGlobals kg, ccl_private const ShaderData *sd) +ccl_device Spectrum shader_bsdf_glossy(KernelGlobals kg, ccl_private const ShaderData *sd) { - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); for (int i = 0; i < sd->num_closure; i++) { ccl_private const ShaderClosure *sc = &sd->closure[i]; @@ -452,9 +450,9 @@ ccl_device float3 shader_bsdf_glossy(KernelGlobals kg, ccl_private const ShaderD return eval; } -ccl_device float3 shader_bsdf_transmission(KernelGlobals kg, ccl_private const ShaderData *sd) +ccl_device Spectrum shader_bsdf_transmission(KernelGlobals kg, ccl_private const ShaderData *sd) { - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); for (int i = 0; i < sd->num_closure; i++) { ccl_private const ShaderClosure *sc = &sd->closure[i]; @@ -479,12 +477,12 @@ ccl_device float3 shader_bsdf_average_normal(KernelGlobals kg, ccl_private const return (is_zero(N)) ? sd->N : normalize(N); } -ccl_device float3 shader_bsdf_ao(KernelGlobals kg, - ccl_private const ShaderData *sd, - const float ao_factor, - ccl_private float3 *N_) +ccl_device Spectrum shader_bsdf_ao(KernelGlobals kg, + ccl_private const ShaderData *sd, + const float ao_factor, + ccl_private float3 *N_) { - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); float3 N = zero_float3(); for (int i = 0; i < sd->num_closure; i++) { @@ -525,15 +523,17 @@ ccl_device float3 shader_bssrdf_normal(ccl_private const ShaderData *sd) ccl_device bool shader_constant_emission_eval(KernelGlobals kg, int shader, - ccl_private float3 *eval) + ccl_private Spectrum *eval) { int shader_index = shader & SHADER_MASK; int shader_flag = kernel_data_fetch(shaders, shader_index).flags; if (shader_flag & SD_HAS_CONSTANT_EMISSION) { - *eval = make_float3(kernel_data_fetch(shaders, shader_index).constant_emission[0], - kernel_data_fetch(shaders, shader_index).constant_emission[1], - kernel_data_fetch(shaders, shader_index).constant_emission[2]); + const float3 emission_rgb = make_float3( + kernel_data_fetch(shaders, shader_index).constant_emission[0], + kernel_data_fetch(shaders, shader_index).constant_emission[1], + kernel_data_fetch(shaders, shader_index).constant_emission[2]); + *eval = rgb_to_spectrum(emission_rgb); return true; } @@ -543,39 +543,39 @@ ccl_device bool shader_constant_emission_eval(KernelGlobals kg, /* Background */ -ccl_device float3 shader_background_eval(ccl_private const ShaderData *sd) +ccl_device Spectrum shader_background_eval(ccl_private const ShaderData *sd) { if (sd->flag & SD_EMISSION) { return sd->closure_emission_background; } else { - return zero_float3(); + return zero_spectrum(); } } /* Emission */ -ccl_device float3 shader_emissive_eval(ccl_private const ShaderData *sd) +ccl_device Spectrum shader_emissive_eval(ccl_private const ShaderData *sd) { if (sd->flag & SD_EMISSION) { return emissive_simple_eval(sd->Ng, sd->I) * sd->closure_emission_background; } else { - return zero_float3(); + return zero_spectrum(); } } /* Holdout */ -ccl_device float3 shader_holdout_apply(KernelGlobals kg, ccl_private ShaderData *sd) +ccl_device Spectrum shader_holdout_apply(KernelGlobals kg, ccl_private ShaderData *sd) { - float3 weight = zero_float3(); + Spectrum weight = zero_spectrum(); /* For objects marked as holdout, preserve transparency and remove all other * closures, replacing them with a holdout weight. */ if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { if ((sd->flag & SD_TRANSPARENT) && !(sd->flag & SD_HAS_ONLY_VOLUME)) { - weight = one_float3() - sd->closure_transparent_extinction; + weight = one_spectrum() - sd->closure_transparent_extinction; for (int i = 0; i < sd->num_closure; i++) { ccl_private ShaderClosure *sc = &sd->closure[i]; @@ -587,7 +587,7 @@ ccl_device float3 shader_holdout_apply(KernelGlobals kg, ccl_private ShaderData sd->flag &= ~(SD_CLOSURE_FLAGS - (SD_TRANSPARENT | SD_BSDF)); } else { - weight = one_float3(); + weight = one_spectrum(); } } else { @@ -642,12 +642,12 @@ ccl_device void shader_eval_surface(KernelGlobals kg, svm_eval_nodes<node_feature_mask, SHADER_TYPE_SURFACE>(kg, state, sd, buffer, path_flag); #else if (sd->object == OBJECT_NONE) { - sd->closure_emission_background = make_float3(0.8f, 0.8f, 0.8f); + sd->closure_emission_background = make_spectrum(0.8f); sd->flag |= SD_EMISSION; } else { ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( - sd, sizeof(DiffuseBsdf), make_float3(0.8f, 0.8f, 0.8f)); + sd, sizeof(DiffuseBsdf), make_spectrum(0.8f)); if (bsdf != NULL) { bsdf->N = sd->N; sd->flag |= bsdf_diffuse_setup(bsdf); @@ -676,7 +676,7 @@ ccl_device_inline float _shader_volume_phase_multi_eval( ccl_private const ShaderVolumeClosure *svc = &phases->closure[i]; float phase_pdf = 0.0f; - float3 eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf); + Spectrum eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf); if (phase_pdf != 0.0f) { bsdf_eval_accum(result_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); @@ -695,7 +695,7 @@ ccl_device float shader_volume_phase_eval(KernelGlobals kg, const float3 omega_in, ccl_private BsdfEval *phase_eval) { - bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_float3()); + bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, zero_spectrum()); return _shader_volume_phase_multi_eval(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f); } @@ -707,7 +707,6 @@ ccl_device int shader_volume_phase_sample(KernelGlobals kg, float randv, ccl_private BsdfEval *phase_eval, ccl_private float3 *omega_in, - ccl_private differential3 *domega_in, ccl_private float *pdf) { int sampled = 0; @@ -747,10 +746,10 @@ ccl_device int shader_volume_phase_sample(KernelGlobals kg, * depending on color channels, even if this is perhaps not a common case */ ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; int label; - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); *pdf = 0.0f; - label = volume_phase_sample(sd, svc, randu, randv, &eval, omega_in, domega_in, pdf); + label = volume_phase_sample(sd, svc, randu, randv, &eval, omega_in, pdf); if (*pdf != 0.0f) { bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); @@ -766,14 +765,13 @@ ccl_device int shader_phase_sample_closure(KernelGlobals kg, float randv, ccl_private BsdfEval *phase_eval, ccl_private float3 *omega_in, - ccl_private differential3 *domega_in, ccl_private float *pdf) { int label; - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); *pdf = 0.0f; - label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); + label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, pdf); if (*pdf != 0.0f) bsdf_eval_init(phase_eval, CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID, eval); diff --git a/intern/cycles/kernel/integrator/shadow_catcher.h b/intern/cycles/kernel/integrator/shadow_catcher.h index ff63625aceb..7103b6032ac 100644 --- a/intern/cycles/kernel/integrator/shadow_catcher.h +++ b/intern/cycles/kernel/integrator/shadow_catcher.h @@ -93,7 +93,7 @@ ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data( /* Since the split is done, the sample does not contribute to the matte, so accumulate it as * transparency to the matte. */ - const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const Spectrum throughput = INTEGRATOR_STATE(state, path, throughput); kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, average(throughput)); } diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h index c340467606d..3b490ecffdd 100644 --- a/intern/cycles/kernel/integrator/shadow_state_template.h +++ b/intern/cycles/kernel/integrator/shadow_state_template.h @@ -27,15 +27,15 @@ KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, queued_kernel, KERNEL_FEATURE_PATH_T /* enum PathRayFlag */ KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) /* Throughput. */ -KERNEL_STRUCT_MEMBER(shadow_path, packed_float3, throughput, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_path, PackedSpectrum, throughput, KERNEL_FEATURE_PATH_TRACING) /* Throughput for shadow pass. */ KERNEL_STRUCT_MEMBER(shadow_path, - packed_float3, + PackedSpectrum, unshadowed_throughput, KERNEL_FEATURE_SHADOW_PASS | KERNEL_FEATURE_AO_ADDITIVE) /* Ratio of throughput to distinguish diffuse / glossy / transmission render passes. */ -KERNEL_STRUCT_MEMBER(shadow_path, packed_float3, pass_diffuse_weight, KERNEL_FEATURE_LIGHT_PASSES) -KERNEL_STRUCT_MEMBER(shadow_path, packed_float3, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES) +KERNEL_STRUCT_MEMBER(shadow_path, PackedSpectrum, pass_diffuse_weight, KERNEL_FEATURE_LIGHT_PASSES) +KERNEL_STRUCT_MEMBER(shadow_path, PackedSpectrum, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES) /* Number of intersections found by ray-tracing. */ KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING) /* Light group. */ diff --git a/intern/cycles/kernel/integrator/state_template.h b/intern/cycles/kernel/integrator/state_template.h index 5c2af131945..f4e280e4cb2 100644 --- a/intern/cycles/kernel/integrator/state_template.h +++ b/intern/cycles/kernel/integrator/state_template.h @@ -46,12 +46,12 @@ KERNEL_STRUCT_MEMBER(path, float, min_ray_pdf, KERNEL_FEATURE_PATH_TRACING) /* Continuation probability for path termination. */ KERNEL_STRUCT_MEMBER(path, float, continuation_probability, KERNEL_FEATURE_PATH_TRACING) /* Throughput. */ -KERNEL_STRUCT_MEMBER(path, packed_float3, throughput, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(path, PackedSpectrum, throughput, KERNEL_FEATURE_PATH_TRACING) /* Ratio of throughput to distinguish diffuse / glossy / transmission render passes. */ -KERNEL_STRUCT_MEMBER(path, packed_float3, pass_diffuse_weight, KERNEL_FEATURE_LIGHT_PASSES) -KERNEL_STRUCT_MEMBER(path, packed_float3, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES) +KERNEL_STRUCT_MEMBER(path, PackedSpectrum, pass_diffuse_weight, KERNEL_FEATURE_LIGHT_PASSES) +KERNEL_STRUCT_MEMBER(path, PackedSpectrum, pass_glossy_weight, KERNEL_FEATURE_LIGHT_PASSES) /* Denoising. */ -KERNEL_STRUCT_MEMBER(path, packed_float3, denoising_feature_throughput, KERNEL_FEATURE_DENOISING) +KERNEL_STRUCT_MEMBER(path, PackedSpectrum, denoising_feature_throughput, KERNEL_FEATURE_DENOISING) /* Shader sorting. */ /* TODO: compress as uint16? or leave out entirely and recompute key in sorting code? */ KERNEL_STRUCT_MEMBER(path, uint32_t, shader_sort_key, KERNEL_FEATURE_PATH_TRACING) @@ -84,8 +84,8 @@ KERNEL_STRUCT_END(isect) /*************** Subsurface closure state for subsurface kernel ***************/ KERNEL_STRUCT_BEGIN(subsurface) -KERNEL_STRUCT_MEMBER(subsurface, packed_float3, albedo, KERNEL_FEATURE_SUBSURFACE) -KERNEL_STRUCT_MEMBER(subsurface, packed_float3, radius, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, PackedSpectrum, albedo, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, PackedSpectrum, radius, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_MEMBER(subsurface, float, anisotropy, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_MEMBER(subsurface, packed_float3, Ng, KERNEL_FEATURE_SUBSURFACE) KERNEL_STRUCT_END(subsurface) diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h index 2f96f215d8a..ee3a619f968 100644 --- a/intern/cycles/kernel/integrator/subsurface.h +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -51,7 +51,7 @@ ccl_device int subsurface_bounce(KernelGlobals kg, PATH_RAY_SUBSURFACE_RANDOM_WALK); /* Compute weight, optionally including Fresnel from entry point. */ - float3 weight = shader_bssrdf_sample_weight(sd, sc); + Spectrum weight = shader_bssrdf_sample_weight(sd, sc); # ifdef __PRINCIPLED__ if (bssrdf->roughness != FLT_MAX) { path_flag |= PATH_RAY_SUBSURFACE_USE_FRESNEL; @@ -70,8 +70,8 @@ ccl_device int subsurface_bounce(KernelGlobals kg, if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { if (INTEGRATOR_STATE(state, path, bounce) == 0) { - INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_float3(); - INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = zero_float3(); + INTEGRATOR_STATE_WRITE(state, path, pass_diffuse_weight) = one_spectrum(); + INTEGRATOR_STATE_WRITE(state, path, pass_glossy_weight) = zero_spectrum(); } } @@ -99,7 +99,7 @@ ccl_device void subsurface_shader_data_setup(KernelGlobals kg, sd->num_closure = 0; sd->num_closure_left = kernel_data.max_closures; - const float3 weight = one_float3(); + const Spectrum weight = one_spectrum(); # ifdef __PRINCIPLED__ if (path_flag & PATH_RAY_SUBSURFACE_USE_FRESNEL) { diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h index 60b63c075a0..77763f702d8 100644 --- a/intern/cycles/kernel/integrator/subsurface_disk.h +++ b/intern/cycles/kernel/integrator/subsurface_disk.h @@ -9,11 +9,11 @@ CCL_NAMESPACE_BEGIN * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf */ -ccl_device_inline float3 subsurface_disk_eval(const float3 radius, float disk_r, float r) +ccl_device_inline Spectrum subsurface_disk_eval(const Spectrum radius, float disk_r, float r) { - const float3 eval = bssrdf_eval(radius, r); + const Spectrum eval = bssrdf_eval(radius, r); const float pdf = bssrdf_pdf(radius, disk_r); - return (pdf > 0.0f) ? eval / pdf : zero_float3(); + return (pdf > 0.0f) ? eval / pdf : zero_spectrum(); } /* Subsurface scattering step, from a point on the surface to other @@ -37,7 +37,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); /* Read subsurface scattering parameters. */ - const float3 radius = INTEGRATOR_STATE(state, subsurface, radius); + const Spectrum radius = INTEGRATOR_STATE(state, subsurface, radius); /* Pick random axis in local frame and point on disk. */ float3 disk_N, disk_T, disk_B; @@ -108,7 +108,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, * traversal algorithm. */ sort_intersections_and_normals(ss_isect.hits, ss_isect.Ng, num_eval_hits); - float3 weights[BSSRDF_MAX_HITS]; /* TODO: zero? */ + Spectrum weights[BSSRDF_MAX_HITS]; /* TODO: zero? */ float sum_weights = 0.0f; for (int hit = 0; hit < num_eval_hits; hit++) { @@ -150,7 +150,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, const float r = len(hit_P - P); /* Evaluate profiles. */ - const float3 weight = subsurface_disk_eval(radius, disk_r, r) * w; + const Spectrum weight = subsurface_disk_eval(radius, disk_r, r) * w; /* Store result. */ ss_isect.Ng[hit] = hit_Ng; @@ -167,7 +167,7 @@ ccl_device_inline bool subsurface_disk(KernelGlobals kg, float partial_sum = 0.0f; for (int hit = 0; hit < num_eval_hits; hit++) { - const float3 weight = weights[hit]; + const Spectrum weight = weights[hit]; const float sample_weight = average(fabs(weight)); float next_sum = partial_sum + sample_weight; diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h index e43bbb3c50a..9c67d909bd4 100644 --- a/intern/cycles/kernel/integrator/subsurface_random_walk.h +++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h @@ -65,19 +65,20 @@ ccl_device void subsurface_random_walk_remap(const float albedo, *sigma_t = sigma_t_prime / (1.0f - g); } -ccl_device void subsurface_random_walk_coefficients(const float3 albedo, - const float3 radius, +ccl_device void subsurface_random_walk_coefficients(const Spectrum albedo, + const Spectrum radius, const float anisotropy, - ccl_private float3 *sigma_t, - ccl_private float3 *alpha, - ccl_private float3 *throughput) + ccl_private Spectrum *sigma_t, + ccl_private Spectrum *alpha, + ccl_private Spectrum *throughput) { - float sigma_t_x, sigma_t_y, sigma_t_z; - float alpha_x, alpha_y, alpha_z; - - subsurface_random_walk_remap(albedo.x, radius.x, anisotropy, &sigma_t_x, &alpha_x); - subsurface_random_walk_remap(albedo.y, radius.y, anisotropy, &sigma_t_y, &alpha_y); - subsurface_random_walk_remap(albedo.z, radius.z, anisotropy, &sigma_t_z, &alpha_z); + FOREACH_SPECTRUM_CHANNEL (i) { + subsurface_random_walk_remap(GET_SPECTRUM_CHANNEL(albedo, i), + GET_SPECTRUM_CHANNEL(radius, i), + anisotropy, + &GET_SPECTRUM_CHANNEL(*sigma_t, i), + &GET_SPECTRUM_CHANNEL(*alpha, i)); + } /* Throughput already contains closure weight at this point, which includes the * albedo, as well as closure mixing and Fresnel weights. Divide out the albedo @@ -88,21 +89,12 @@ ccl_device void subsurface_random_walk_coefficients(const float3 albedo, * infinite phase functions. To avoid a sharp discontinuity as we go from * such values to 0.0, increase alpha and reduce the throughput to compensate. */ const float min_alpha = 0.2f; - if (alpha_x < min_alpha) { - (*throughput).x *= alpha_x / min_alpha; - alpha_x = min_alpha; - } - if (alpha_y < min_alpha) { - (*throughput).y *= alpha_y / min_alpha; - alpha_y = min_alpha; - } - if (alpha_z < min_alpha) { - (*throughput).z *= alpha_z / min_alpha; - alpha_z = min_alpha; + FOREACH_SPECTRUM_CHANNEL (i) { + if (GET_SPECTRUM_CHANNEL(*alpha, i) < min_alpha) { + GET_SPECTRUM_CHANNEL(*throughput, i) *= GET_SPECTRUM_CHANNEL(*alpha, i) / min_alpha; + GET_SPECTRUM_CHANNEL(*alpha, i) = min_alpha; + } } - - *sigma_t = make_float3(sigma_t_x, sigma_t_y, sigma_t_z); - *alpha = make_float3(alpha_x, alpha_y, alpha_z); } /* References for Dwivedi sampling: @@ -151,12 +143,12 @@ ccl_device_forceinline float3 direction_from_cosine(float3 D, float cos_theta, f return dir.x * T + dir.y * B + dir.z * D; } -ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, - float t, - bool hit, - ccl_private float3 *transmittance) +ccl_device_forceinline Spectrum subsurface_random_walk_pdf(Spectrum sigma_t, + float t, + bool hit, + ccl_private Spectrum *transmittance) { - float3 T = volume_color_transmittance(sigma_t, t); + Spectrum T = volume_color_transmittance(sigma_t, t); if (transmittance) { *transmittance = T; } @@ -207,14 +199,14 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, /* Convert subsurface to volume coefficients. * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ - const float3 albedo = INTEGRATOR_STATE(state, subsurface, albedo); - const float3 radius = INTEGRATOR_STATE(state, subsurface, radius); + const Spectrum albedo = INTEGRATOR_STATE(state, subsurface, albedo); + const Spectrum radius = INTEGRATOR_STATE(state, subsurface, radius); const float anisotropy = INTEGRATOR_STATE(state, subsurface, anisotropy); - float3 sigma_t, alpha; - float3 throughput = INTEGRATOR_STATE_WRITE(state, path, throughput); + Spectrum sigma_t, alpha; + Spectrum throughput = INTEGRATOR_STATE_WRITE(state, path, throughput); subsurface_random_walk_coefficients(albedo, radius, anisotropy, &sigma_t, &alpha, &throughput); - float3 sigma_s = sigma_t * alpha; + Spectrum sigma_s = sigma_t * alpha; /* Theoretically it should be better to use the exact alpha for the channel we're sampling at * each bounce, but in practice there doesn't seem to be a noticeable difference in exchange @@ -249,10 +241,10 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, const float guided_fraction = 1.0f - fmaxf(0.5f, powf(fabsf(anisotropy), 0.125f)); #ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL - float3 sigma_s_star = sigma_s * (1.0f - anisotropy); - float3 sigma_t_star = sigma_t - sigma_s + sigma_s_star; - float3 sigma_t_org = sigma_t; - float3 sigma_s_org = sigma_s; + Spectrum sigma_s_star = sigma_s * (1.0f - anisotropy); + Spectrum sigma_t_star = sigma_t - sigma_s + sigma_s_star; + Spectrum sigma_t_org = sigma_t; + Spectrum sigma_s_org = sigma_s; const float anisotropy_org = anisotropy; const float guided_fraction_org = guided_fraction; #endif @@ -264,7 +256,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, #ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL // shadow with local variables according to depth float anisotropy, guided_fraction; - float3 sigma_s, sigma_t; + Spectrum sigma_s, sigma_t; if (bounce <= SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL) { anisotropy = anisotropy_org; guided_fraction = guided_fraction_org; @@ -281,7 +273,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, /* Sample color channel, use MIS with balance heuristic. */ float rphase = path_state_rng_1D(kg, &rng_state, PRNG_PHASE_CHANNEL); - float3 channel_pdf; + Spectrum channel_pdf; int channel = volume_sample_channel(alpha, throughput, rphase, &channel_pdf); float sample_sigma_t = volume_channel_get(sigma_t, channel); float randt = path_state_rng_1D(kg, &rng_state, PRNG_SCATTER_DISTANCE); @@ -399,16 +391,17 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, /* Advance to new scatter location. */ ray.P += t * ray.D; - float3 transmittance; - float3 pdf = subsurface_random_walk_pdf(sigma_t, t, hit, &transmittance); + Spectrum transmittance; + Spectrum pdf = subsurface_random_walk_pdf(sigma_t, t, hit, &transmittance); if (bounce > 0) { /* Compute PDF just like we do for classic sampling, but with the stretched sigma_t. */ - float3 guided_pdf = subsurface_random_walk_pdf(forward_stretching * sigma_t, t, hit, NULL); + Spectrum guided_pdf = subsurface_random_walk_pdf(forward_stretching * sigma_t, t, hit, NULL); if (have_opposite_interface) { /* First step of MIS: Depending on geometry we might have two methods for guided * sampling, so perform MIS between them. */ - float3 back_pdf = subsurface_random_walk_pdf(backward_stretching * sigma_t, t, hit, NULL); + Spectrum back_pdf = subsurface_random_walk_pdf( + backward_stretching * sigma_t, t, hit, NULL); guided_pdf = mix( guided_pdf * forward_pdf_factor, back_pdf * backward_pdf_factor, backward_fraction); } @@ -430,9 +423,7 @@ ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, /* If we hit the surface, we are done. */ break; } - else if (throughput.x < VOLUME_THROUGHPUT_EPSILON && - throughput.y < VOLUME_THROUGHPUT_EPSILON && - throughput.z < VOLUME_THROUGHPUT_EPSILON) { + else if (reduce_max(throughput) < VOLUME_THROUGHPUT_EPSILON) { /* Avoid unnecessary work and precision issue when throughput gets really small. */ break; } diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h index 2e309cee43f..4195675dd13 100644 --- a/intern/cycles/kernel/light/sample.h +++ b/intern/cycles/kernel/light/sample.h @@ -14,7 +14,7 @@ CCL_NAMESPACE_BEGIN /* Evaluate shader on light. */ -ccl_device_noinline_cpu float3 +ccl_device_noinline_cpu Spectrum light_sample_shader_eval(KernelGlobals kg, IntegratorState state, ccl_private ShaderData *ccl_restrict emission_sd, @@ -22,7 +22,7 @@ light_sample_shader_eval(KernelGlobals kg, float time) { /* setup shading at emitter */ - float3 eval = zero_float3(); + Spectrum eval = zero_spectrum(); if (shader_constant_emission_eval(kg, ls->shader, &eval)) { if ((ls->prim != PRIM_NONE) && dot(ls->Ng, ls->D) > 0.0f) { @@ -82,7 +82,8 @@ light_sample_shader_eval(KernelGlobals kg, if (ls->lamp != LAMP_NONE) { ccl_global const KernelLight *klight = &kernel_data_fetch(lights, ls->lamp); - eval *= make_float3(klight->strength[0], klight->strength[1], klight->strength[2]); + eval *= rgb_to_spectrum( + make_float3(klight->strength[0], klight->strength[1], klight->strength[2])); } return eval; diff --git a/intern/cycles/kernel/osl/background.cpp b/intern/cycles/kernel/osl/background.cpp index 865ff4ddc6d..4b5a2686117 100644 --- a/intern/cycles/kernel/osl/background.cpp +++ b/intern/cycles/kernel/osl/background.cpp @@ -14,8 +14,12 @@ // clang-format off #include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + #include "kernel/closure/alloc.h" #include "kernel/closure/emissive.h" + +#include "kernel/util/color.h" // clang-format on CCL_NAMESPACE_BEGIN @@ -32,7 +36,7 @@ class GenericBackgroundClosure : public CClosurePrimitive { public: void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { - background_setup(sd, weight); + background_setup(sd, rgb_to_spectrum(weight)); } }; @@ -47,7 +51,7 @@ class HoldoutClosure : CClosurePrimitive { public: void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { - closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, weight); + closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, rgb_to_spectrum(weight)); sd->flag |= SD_HOLDOUT; } }; diff --git a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp index 39fcee1ac0d..667207ec6bf 100644 --- a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp @@ -14,10 +14,15 @@ #include "kernel/osl/closures.h" // clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + #include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_diffuse_ramp.h" #include "kernel/closure/bsdf_util.h" + +#include "kernel/util/color.h" // clang-format on CCL_NAMESPACE_BEGIN @@ -34,7 +39,7 @@ class DiffuseRampClosure : public CBSDFClosure { params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); DiffuseRampBsdf *bsdf = (DiffuseRampBsdf *)bsdf_alloc_osl( - sd, sizeof(DiffuseRampBsdf), weight, ¶ms); + sd, sizeof(DiffuseRampBsdf), rgb_to_spectrum(weight), ¶ms); if (bsdf) { bsdf->colors = (float3 *)closure_alloc_extra(sd, sizeof(float3) * 8); diff --git a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp index 972ed7e4a6d..6f54a96e542 100644 --- a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp @@ -14,10 +14,15 @@ #include "kernel/osl/closures.h" // clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + #include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_phong_ramp.h" #include "kernel/closure/bsdf_util.h" + +#include "kernel/util/color.h" // clang-format on CCL_NAMESPACE_BEGIN @@ -34,7 +39,7 @@ class PhongRampClosure : public CBSDFClosure { params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); PhongRampBsdf *bsdf = (PhongRampBsdf *)bsdf_alloc_osl( - sd, sizeof(PhongRampBsdf), weight, ¶ms); + sd, sizeof(PhongRampBsdf), rgb_to_spectrum(weight), ¶ms); if (bsdf) { bsdf->colors = (float3 *)closure_alloc_extra(sd, sizeof(float3) * 8); diff --git a/intern/cycles/kernel/osl/bssrdf.cpp b/intern/cycles/kernel/osl/bssrdf.cpp index 4b282fddad3..3054946ba5a 100644 --- a/intern/cycles/kernel/osl/bssrdf.cpp +++ b/intern/cycles/kernel/osl/bssrdf.cpp @@ -12,6 +12,9 @@ #include "kernel/osl/closures.h" // clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + #include "kernel/types.h" #include "kernel/closure/alloc.h" @@ -19,6 +22,8 @@ #include "kernel/closure/bsdf_diffuse.h" #include "kernel/closure/bsdf_principled_diffuse.h" #include "kernel/closure/bssrdf.h" + +#include "kernel/util/color.h" // clang-format on CCL_NAMESPACE_BEGIN @@ -59,14 +64,14 @@ class CBSSRDFClosure : public CClosurePrimitive { void alloc(ShaderData *sd, uint32_t path_flag, float3 weight, ClosureType type) { - Bssrdf *bssrdf = bssrdf_alloc(sd, weight); + Bssrdf *bssrdf = bssrdf_alloc(sd, rgb_to_spectrum(weight)); if (bssrdf) { /* disable in case of diffuse ancestor, can't see it well then and * adds considerably noise due to probabilities of continuing path * getting lower and lower */ if (path_flag & PATH_RAY_DIFFUSE_ANCESTOR) { - params.radius = make_float3(0.0f, 0.0f, 0.0f); + params.radius = zero_spectrum(); } /* create one closure per color channel */ diff --git a/intern/cycles/kernel/osl/closures.cpp b/intern/cycles/kernel/osl/closures.cpp index 7c6b48154e4..8766fb73dbb 100644 --- a/intern/cycles/kernel/osl/closures.cpp +++ b/intern/cycles/kernel/osl/closures.cpp @@ -38,6 +38,8 @@ #include "kernel/closure/bsdf_principled_diffuse.h" #include "kernel/closure/bsdf_principled_sheen.h" #include "kernel/closure/volume.h" + +#include "kernel/util/color.h" // clang-format on CCL_NAMESPACE_BEGIN @@ -183,7 +185,7 @@ class PrincipledSheenClosure : public CBSDFClosure { params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf *)bsdf_alloc_osl( - sd, sizeof(PrincipledSheenBsdf), weight, ¶ms); + sd, sizeof(PrincipledSheenBsdf), rgb_to_spectrum(weight), ¶ms); sd->flag |= (bsdf) ? bsdf_principled_sheen_setup(sd, bsdf) : 0; } } @@ -207,7 +209,7 @@ class PrincipledHairClosure : public CBSDFClosure { PrincipledHairBSDF *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)bsdf_alloc_osl( - sd, sizeof(PrincipledHairBSDF), weight, ¶ms); + sd, sizeof(PrincipledHairBSDF), rgb_to_spectrum(weight), ¶ms); if (!bsdf) { return NULL; } @@ -263,7 +265,7 @@ class PrincipledClearcoatClosure : public CBSDFClosure { MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), weight, ¶ms); + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); if (!bsdf) { return NULL; } @@ -273,13 +275,13 @@ class PrincipledClearcoatClosure : public CBSDFClosure { return NULL; } - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->extra = extra; bsdf->ior = 1.5f; bsdf->alpha_x = clearcoat_roughness; bsdf->alpha_y = clearcoat_roughness; - bsdf->extra->color = make_float3(0.0f, 0.0f, 0.0f); - bsdf->extra->cspec0 = make_float3(0.04f, 0.04f, 0.04f); + bsdf->extra->color = zero_spectrum(); + bsdf->extra->cspec0 = make_spectrum(0.04f); bsdf->extra->clearcoat = clearcoat; return bsdf; } @@ -511,7 +513,7 @@ class MicrofacetClosure : public CBSDFClosure { params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), weight, ¶ms); + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); if (!bsdf) { return; @@ -586,7 +588,7 @@ class MicrofacetFresnelClosure : public CBSDFClosure { } MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), weight, ¶ms); + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); if (!bsdf) { return NULL; } @@ -597,8 +599,8 @@ class MicrofacetFresnelClosure : public CBSDFClosure { } bsdf->extra = extra; - bsdf->extra->color = color; - bsdf->extra->cspec0 = cspec0; + bsdf->extra->color = rgb_to_spectrum(color); + bsdf->extra->cspec0 = rgb_to_spectrum(cspec0); bsdf->extra->clearcoat = 0.0f; return bsdf; } @@ -615,7 +617,7 @@ class MicrofacetGGXFresnelClosure : public MicrofacetFresnelClosure { return; } - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->alpha_y = bsdf->alpha_x; sd->flag |= bsdf_microfacet_ggx_fresnel_setup(bsdf, sd); } @@ -684,7 +686,7 @@ class MicrofacetMultiClosure : public CBSDFClosure { } MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), weight, ¶ms); + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); if (!bsdf) { return NULL; } @@ -695,8 +697,8 @@ class MicrofacetMultiClosure : public CBSDFClosure { } bsdf->extra = extra; - bsdf->extra->color = color; - bsdf->extra->cspec0 = make_float3(0.0f, 0.0f, 0.0f); + bsdf->extra->color = rgb_to_spectrum(color); + bsdf->extra->cspec0 = zero_spectrum(); bsdf->extra->clearcoat = 0.0f; return bsdf; } @@ -714,7 +716,7 @@ class MicrofacetMultiGGXClosure : public MicrofacetMultiClosure { } bsdf->ior = 0.0f; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->alpha_y = bsdf->alpha_x; sd->flag |= bsdf_microfacet_multi_ggx_setup(bsdf); } @@ -777,7 +779,7 @@ class MicrofacetMultiGGXGlassClosure : public MicrofacetMultiClosure { return; } - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->alpha_y = bsdf->alpha_x; sd->flag |= bsdf_microfacet_multi_ggx_glass_setup(bsdf); } @@ -814,7 +816,7 @@ class MicrofacetMultiFresnelClosure : public CBSDFClosure { } MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( - sd, sizeof(MicrofacetBsdf), weight, ¶ms); + sd, sizeof(MicrofacetBsdf), rgb_to_spectrum(weight), ¶ms); if (!bsdf) { return NULL; } @@ -825,8 +827,8 @@ class MicrofacetMultiFresnelClosure : public CBSDFClosure { } bsdf->extra = extra; - bsdf->extra->color = color; - bsdf->extra->cspec0 = cspec0; + bsdf->extra->color = rgb_to_spectrum(color); + bsdf->extra->cspec0 = rgb_to_spectrum(cspec0); bsdf->extra->clearcoat = 0.0f; return bsdf; } @@ -843,7 +845,7 @@ class MicrofacetMultiGGXFresnelClosure : public MicrofacetMultiFresnelClosure { return; } - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->alpha_y = bsdf->alpha_x; sd->flag |= bsdf_microfacet_multi_ggx_fresnel_setup(bsdf, sd); } @@ -911,7 +913,7 @@ class MicrofacetMultiGGXGlassFresnelClosure : public MicrofacetMultiFresnelClosu return; } - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->alpha_y = bsdf->alpha_x; sd->flag |= bsdf_microfacet_multi_ggx_glass_fresnel_setup(bsdf, sd); } @@ -941,7 +943,7 @@ class TransparentClosure : public CBSDFClosure { void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { - bsdf_transparent_setup(sd, weight, path_flag); + bsdf_transparent_setup(sd, rgb_to_spectrum(weight), path_flag); } }; @@ -960,7 +962,7 @@ class VolumeAbsorptionClosure : public CBSDFClosure { public: void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { - volume_extinction_setup(sd, weight); + volume_extinction_setup(sd, rgb_to_spectrum(weight)); } }; @@ -979,10 +981,10 @@ class VolumeHenyeyGreensteinClosure : public CBSDFClosure { void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { - volume_extinction_setup(sd, weight); + volume_extinction_setup(sd, rgb_to_spectrum(weight)); HenyeyGreensteinVolume *volume = (HenyeyGreensteinVolume *)bsdf_alloc_osl( - sd, sizeof(HenyeyGreensteinVolume), weight, ¶ms); + sd, sizeof(HenyeyGreensteinVolume), rgb_to_spectrum(weight), ¶ms); if (!volume) { return; } diff --git a/intern/cycles/kernel/osl/closures.h b/intern/cycles/kernel/osl/closures.h index e10a3d88a04..97666be7a1e 100644 --- a/intern/cycles/kernel/osl/closures.h +++ b/intern/cycles/kernel/osl/closures.h @@ -115,7 +115,8 @@ class CBSDFClosure : public CClosurePrimitive { { \ if (!skip(sd, path_flag, TYPE)) { \ params.N = ensure_valid_reflection(sd->Ng, sd->I, params.N); \ - structname *bsdf = (structname *)bsdf_alloc_osl(sd, sizeof(structname), weight, ¶ms); \ + structname *bsdf = (structname *)bsdf_alloc_osl( \ + sd, sizeof(structname), rgb_to_spectrum(weight), ¶ms); \ sd->flag |= (bsdf) ? bsdf_##lower##_setup(bsdf) : 0; \ } \ } \ diff --git a/intern/cycles/kernel/osl/emissive.cpp b/intern/cycles/kernel/osl/emissive.cpp index 1a01b215836..8d1928d0126 100644 --- a/intern/cycles/kernel/osl/emissive.cpp +++ b/intern/cycles/kernel/osl/emissive.cpp @@ -14,9 +14,13 @@ // clang-format off #include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + #include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/emissive.h" + +#include "kernel/util/color.h" // clang-format on CCL_NAMESPACE_BEGIN @@ -34,7 +38,7 @@ class GenericEmissiveClosure : public CClosurePrimitive { public: void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { - emission_setup(sd, weight); + emission_setup(sd, rgb_to_spectrum(weight)); } }; diff --git a/intern/cycles/kernel/osl/services.cpp b/intern/cycles/kernel/osl/services.cpp index 6b7981b7f3a..6766fe2ce89 100644 --- a/intern/cycles/kernel/osl/services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -1102,8 +1102,9 @@ bool OSLRenderServices::get_background_attribute(const KernelGlobalsCPU *kg, ndc[0] = camera_world_to_ndc(kg, sd, sd->P); if (derivatives) { - ndc[1] = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx) - ndc[0]; - ndc[2] = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy) - ndc[0]; + const differential3 dP = differential_from_compact(sd->Ng, sd->dP); + ndc[1] = camera_world_to_ndc(kg, sd, sd->P + dP.dx) - ndc[0]; + ndc[2] = camera_world_to_ndc(kg, sd, sd->P + dP.dy) - ndc[0]; } } @@ -1755,11 +1756,13 @@ bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg, return set_attribute_float3(sd->Ng, type, derivatives, val); } else if (name == u_P) { - float3 f[3] = {sd->P, sd->dP.dx, sd->dP.dy}; + const differential3 dP = differential_from_compact(sd->Ng, sd->dP); + float3 f[3] = {sd->P, dP.dx, dP.dy}; return set_attribute_float3(f, type, derivatives, val); } else if (name == u_I) { - float3 f[3] = {sd->I, sd->dI.dx, sd->dI.dy}; + const differential3 dI = differential_from_compact(sd->I, sd->dI); + float3 f[3] = {sd->I, dI.dx, dI.dy}; return set_attribute_float3(f, type, derivatives, val); } else if (name == u_u) { diff --git a/intern/cycles/kernel/osl/shader.cpp b/intern/cycles/kernel/osl/shader.cpp index af96c0070e3..5862b6a8a2b 100644 --- a/intern/cycles/kernel/osl/shader.cpp +++ b/intern/cycles/kernel/osl/shader.cpp @@ -17,6 +17,8 @@ #include "kernel/osl/globals.h" #include "kernel/osl/services.h" #include "kernel/osl/shader.h" + +#include "kernel/util/differential.h" // clang-format on #include "scene/attribute.h" @@ -79,13 +81,16 @@ static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, { OSL::ShaderGlobals *globals = &tdata->globals; + const differential3 dP = differential_from_compact(sd->Ng, sd->dP); + const differential3 dI = differential_from_compact(sd->I, sd->dI); + /* copy from shader data to shader globals */ globals->P = TO_VEC3(sd->P); - globals->dPdx = TO_VEC3(sd->dP.dx); - globals->dPdy = TO_VEC3(sd->dP.dy); + globals->dPdx = TO_VEC3(dP.dx); + globals->dPdy = TO_VEC3(dP.dy); globals->I = TO_VEC3(sd->I); - globals->dIdx = TO_VEC3(sd->dI.dx); - globals->dIdy = TO_VEC3(sd->dI.dy); + globals->dIdx = TO_VEC3(dI.dx); + globals->dIdy = TO_VEC3(dI.dy); globals->N = TO_VEC3(sd->N); globals->Ng = TO_VEC3(sd->Ng); globals->u = sd->u; @@ -183,9 +188,10 @@ void OSLShader::eval_surface(const KernelGlobalsCPU *kg, /* automatic bump shader */ if (kg->osl->bump_state[shader]) { /* save state */ - float3 P = sd->P; - float3 dPdx = sd->dP.dx; - float3 dPdy = sd->dP.dy; + const float3 P = sd->P; + const float dP = sd->dP; + const OSL::Vec3 dPdx = globals->dPdx; + const OSL::Vec3 dPdy = globals->dPdy; /* set state as if undisplaced */ if (sd->flag & SD_HAS_DISPLACEMENT) { @@ -199,17 +205,20 @@ void OSLShader::eval_surface(const KernelGlobalsCPU *kg, (void)found; assert(found); + differential3 tmp_dP; memcpy(&sd->P, data, sizeof(float) * 3); - memcpy(&sd->dP.dx, data + 3, sizeof(float) * 3); - memcpy(&sd->dP.dy, data + 6, sizeof(float) * 3); + memcpy(&tmp_dP.dx, data + 3, sizeof(float) * 3); + memcpy(&tmp_dP.dy, data + 6, sizeof(float) * 3); object_position_transform(kg, sd, &sd->P); - object_dir_transform(kg, sd, &sd->dP.dx); - object_dir_transform(kg, sd, &sd->dP.dy); + object_dir_transform(kg, sd, &tmp_dP.dx); + object_dir_transform(kg, sd, &tmp_dP.dy); + + sd->dP = differential_make_compact(tmp_dP); globals->P = TO_VEC3(sd->P); - globals->dPdx = TO_VEC3(sd->dP.dx); - globals->dPdy = TO_VEC3(sd->dP.dy); + globals->dPdx = TO_VEC3(tmp_dP.dx); + globals->dPdy = TO_VEC3(tmp_dP.dy); } /* execute bump shader */ @@ -217,8 +226,7 @@ void OSLShader::eval_surface(const KernelGlobalsCPU *kg, /* reset state */ sd->P = P; - sd->dP.dx = dPdx; - sd->dP.dy = dPdy; + sd->dP = dP; globals->P = TO_VEC3(P); globals->dPdx = TO_VEC3(dPdx); diff --git a/intern/cycles/kernel/svm/attribute.h b/intern/cycles/kernel/svm/attribute.h index a3609d8b4b0..5f0d1609f08 100644 --- a/intern/cycles/kernel/svm/attribute.h +++ b/intern/cycles/kernel/svm/attribute.h @@ -140,6 +140,16 @@ ccl_device_noinline void svm_node_attr(KernelGlobals kg, } } +ccl_device_forceinline float3 svm_node_bump_P_dx(const ccl_private ShaderData *sd) +{ + return sd->P + differential_from_compact(sd->Ng, sd->dP).dx; +} + +ccl_device_forceinline float3 svm_node_bump_P_dy(const ccl_private ShaderData *sd) +{ + return sd->P + differential_from_compact(sd->Ng, sd->dP).dy; +} + ccl_device_noinline void svm_node_attr_bump_dx(KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, @@ -167,7 +177,7 @@ ccl_device_noinline void svm_node_attr_bump_dx(KernelGlobals kg, if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) { /* No generated attribute, fall back to object coordinates. */ - float3 f = sd->P + sd->dP.dx; + float3 f = svm_node_bump_P_dx(sd); if (sd->object != OBJECT_NONE) { object_inverse_position_transform(kg, sd, &f); } @@ -265,7 +275,7 @@ ccl_device_noinline void svm_node_attr_bump_dy(KernelGlobals kg, if (node.y == ATTR_STD_GENERATED && desc.element == ATTR_ELEMENT_NONE) { /* No generated attribute, fall back to object coordinates. */ - float3 f = sd->P + sd->dP.dy; + float3 f = svm_node_bump_P_dy(sd); if (sd->object != OBJECT_NONE) { object_inverse_position_transform(kg, sd, &f); } diff --git a/intern/cycles/kernel/svm/bump.h b/intern/cycles/kernel/svm/bump.h index 566c45f5f25..1009a6a4241 100644 --- a/intern/cycles/kernel/svm/bump.h +++ b/intern/cycles/kernel/svm/bump.h @@ -14,23 +14,21 @@ ccl_device_noinline void svm_node_enter_bump_eval(KernelGlobals kg, { /* save state */ stack_store_float3(stack, offset + 0, sd->P); - stack_store_float3(stack, offset + 3, sd->dP.dx); - stack_store_float3(stack, offset + 6, sd->dP.dy); + stack_store_float(stack, offset + 3, sd->dP); /* set state as if undisplaced */ const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_POSITION_UNDISPLACED); if (desc.offset != ATTR_STD_NOT_FOUND) { - float3 P, dPdx, dPdy; - P = primitive_surface_attribute_float3(kg, sd, desc, &dPdx, &dPdy); + differential3 dP; + float3 P = primitive_surface_attribute_float3(kg, sd, desc, &dP.dx, &dP.dy); object_position_transform(kg, sd, &P); - object_dir_transform(kg, sd, &dPdx); - object_dir_transform(kg, sd, &dPdy); + object_dir_transform(kg, sd, &dP.dx); + object_dir_transform(kg, sd, &dP.dy); sd->P = P; - sd->dP.dx = dPdx; - sd->dP.dy = dPdy; + sd->dP = differential_make_compact(dP); } } @@ -41,8 +39,7 @@ ccl_device_noinline void svm_node_leave_bump_eval(KernelGlobals kg, { /* restore state */ sd->P = stack_load_float3(stack, offset + 0); - sd->dP.dx = stack_load_float3(stack, offset + 3); - sd->dP.dy = stack_load_float3(stack, offset + 6); + sd->dP = stack_load_float(stack, offset + 3); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/closure.h b/intern/cycles/kernel/svm/closure.h index 99a8fdd3be9..5e8ef69fd15 100644 --- a/intern/cycles/kernel/svm/closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -3,6 +3,8 @@ #pragma once +#include "kernel/util/color.h" + CCL_NAMESPACE_BEGIN /* Closure Nodes */ @@ -183,7 +185,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, } float3 subsurface_radius = stack_valid(data_cn_ssr.y) ? stack_load_float3(stack, data_cn_ssr.y) : - make_float3(1.0f, 1.0f, 1.0f); + one_float3(); float subsurface_ior = stack_valid(data_cn_ssr.z) ? stack_load_float(stack, data_cn_ssr.z) : 1.4f; float subsurface_anisotropy = stack_valid(data_cn_ssr.w) ? @@ -198,12 +200,12 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, __uint_as_float(data_subsurface_color.z), __uint_as_float(data_subsurface_color.w)); - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; # ifdef __SUBSURFACE__ float3 mixed_ss_base_color = subsurface_color * subsurface + base_color * (1.0f - subsurface); - float3 subsurf_weight = weight * mixed_ss_base_color * diffuse_weight; + Spectrum subsurf_weight = weight * rgb_to_spectrum(mixed_ss_base_color) * diffuse_weight; /* disable in case of diffuse ancestor, can't see it well then and * adds considerably noise due to probabilities of continuing path @@ -220,7 +222,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, /* diffuse */ if (fabsf(average(mixed_ss_base_color)) > CLOSURE_WEIGHT_CUTOFF) { if (subsurface <= CLOSURE_WEIGHT_CUTOFF && diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { - float3 diff_weight = weight * base_color * diffuse_weight; + Spectrum diff_weight = weight * rgb_to_spectrum(base_color) * diffuse_weight; ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *) bsdf_alloc(sd, sizeof(PrincipledDiffuseBsdf), diff_weight); @@ -237,8 +239,8 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, subsurf_weight); if (bssrdf) { - bssrdf->radius = subsurface_radius * subsurface; - bssrdf->albedo = mixed_ss_base_color; + bssrdf->radius = rgb_to_spectrum(subsurface_radius * subsurface); + bssrdf->albedo = rgb_to_spectrum(mixed_ss_base_color); bssrdf->N = N; bssrdf->roughness = roughness; @@ -254,7 +256,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, # else /* diffuse */ if (diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { - float3 diff_weight = weight * base_color * diffuse_weight; + Spectrum diff_weight = weight * rgb_to_spectrum(base_color) * diffuse_weight; ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), diff_weight); @@ -272,15 +274,13 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, /* sheen */ if (diffuse_weight > CLOSURE_WEIGHT_CUTOFF && sheen > CLOSURE_WEIGHT_CUTOFF) { float m_cdlum = linear_rgb_to_gray(kg, base_color); - float3 m_ctint = m_cdlum > 0.0f ? - base_color / m_cdlum : - make_float3(1.0f, 1.0f, 1.0f); // normalize lum. to isolate hue+sat + float3 m_ctint = m_cdlum > 0.0f ? base_color / m_cdlum : + one_float3(); // normalize lum. to isolate hue+sat /* color of the sheen component */ - float3 sheen_color = make_float3(1.0f, 1.0f, 1.0f) * (1.0f - sheen_tint) + - m_ctint * sheen_tint; + float3 sheen_color = make_float3(1.0f - sheen_tint) + m_ctint * sheen_tint; - float3 sheen_weight = weight * sheen * sheen_color * diffuse_weight; + Spectrum sheen_weight = weight * sheen * rgb_to_spectrum(sheen_color) * diffuse_weight; ccl_private PrincipledSheenBsdf *bsdf = (ccl_private PrincipledSheenBsdf *)bsdf_alloc( sd, sizeof(PrincipledSheenBsdf), sheen_weight); @@ -299,7 +299,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, # endif if (specular_weight > CLOSURE_WEIGHT_CUTOFF && (specular > CLOSURE_WEIGHT_CUTOFF || metallic > CLOSURE_WEIGHT_CUTOFF)) { - float3 spec_weight = weight * specular_weight; + Spectrum spec_weight = weight * specular_weight; ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), spec_weight); @@ -322,16 +322,13 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, float m_cdlum = 0.3f * base_color.x + 0.6f * base_color.y + 0.1f * base_color.z; // luminance approx. - float3 m_ctint = m_cdlum > 0.0f ? - base_color / m_cdlum : - make_float3( - 1.0f, 1.0f, 1.0f); // normalize lum. to isolate hue+sat - float3 tmp_col = make_float3(1.0f, 1.0f, 1.0f) * (1.0f - specular_tint) + - m_ctint * specular_tint; - - bsdf->extra->cspec0 = (specular * 0.08f * tmp_col) * (1.0f - metallic) + - base_color * metallic; - bsdf->extra->color = base_color; + float3 m_ctint = m_cdlum > 0.0f ? base_color / m_cdlum : + one_float3(); // normalize lum. to isolate hue+sat + float3 tmp_col = make_float3(1.0f - specular_tint) + m_ctint * specular_tint; + + bsdf->extra->cspec0 = rgb_to_spectrum( + (specular * 0.08f * tmp_col) * (1.0f - metallic) + base_color * metallic); + bsdf->extra->color = rgb_to_spectrum(base_color); bsdf->extra->clearcoat = 0.0f; /* setup bsdf */ @@ -352,9 +349,8 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) { # endif if (final_transmission > CLOSURE_WEIGHT_CUTOFF) { - float3 glass_weight = weight * final_transmission; - float3 cspec0 = base_color * specular_tint + - make_float3(1.0f, 1.0f, 1.0f) * (1.0f - specular_tint); + Spectrum glass_weight = weight * final_transmission; + float3 cspec0 = base_color * specular_tint + make_float3(1.0f - specular_tint); if (roughness <= 5e-2f || distribution == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID) { /* use single-scatter GGX */ @@ -374,15 +370,15 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (bsdf && extra) { bsdf->N = N; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->extra = extra; bsdf->alpha_x = refl_roughness * refl_roughness; bsdf->alpha_y = refl_roughness * refl_roughness; bsdf->ior = ior; - bsdf->extra->color = base_color; - bsdf->extra->cspec0 = cspec0; + bsdf->extra->color = rgb_to_spectrum(base_color); + bsdf->extra->cspec0 = rgb_to_spectrum(cspec0); bsdf->extra->clearcoat = 0.0f; /* setup bsdf */ @@ -398,10 +394,12 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, /* This is to prevent MNEE from receiving a null BSDF. */ float refraction_fresnel = fmaxf(0.0001f, 1.0f - fresnel); ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( - sd, sizeof(MicrofacetBsdf), base_color * glass_weight * refraction_fresnel); + sd, + sizeof(MicrofacetBsdf), + rgb_to_spectrum(base_color) * glass_weight * refraction_fresnel); if (bsdf) { bsdf->N = N; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->extra = NULL; if (distribution == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID) @@ -430,14 +428,14 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (bsdf && extra) { bsdf->N = N; bsdf->extra = extra; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->alpha_x = roughness * roughness; bsdf->alpha_y = roughness * roughness; bsdf->ior = ior; - bsdf->extra->color = base_color; - bsdf->extra->cspec0 = cspec0; + bsdf->extra->color = rgb_to_spectrum(base_color); + bsdf->extra->cspec0 = rgb_to_spectrum(cspec0); bsdf->extra->clearcoat = 0.0f; /* setup bsdf */ @@ -463,15 +461,15 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (bsdf && extra) { bsdf->N = clearcoat_normal; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->ior = 1.5f; bsdf->extra = extra; bsdf->alpha_x = clearcoat_roughness * clearcoat_roughness; bsdf->alpha_y = clearcoat_roughness * clearcoat_roughness; - bsdf->extra->color = make_float3(0.0f, 0.0f, 0.0f); - bsdf->extra->cspec0 = make_float3(0.04f, 0.04f, 0.04f); + bsdf->extra->color = zero_spectrum(); + bsdf->extra->cspec0 = make_spectrum(0.04f); bsdf->extra->clearcoat = clearcoat; /* setup bsdf */ @@ -486,7 +484,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, } #endif /* __PRINCIPLED__ */ case CLOSURE_BSDF_DIFFUSE_ID: { - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)bsdf_alloc( sd, sizeof(OrenNayarBsdf), weight); @@ -506,7 +504,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, break; } case CLOSURE_BSDF_TRANSLUCENT_ID: { - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( sd, sizeof(DiffuseBsdf), weight); @@ -517,7 +515,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, break; } case CLOSURE_BSDF_TRANSPARENT_ID: { - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; bsdf_transparent_setup(sd, weight, path_flag); break; } @@ -530,7 +528,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (!kernel_data.integrator.caustics_reflective && (path_flag & PATH_RAY_DIFFUSE)) break; #endif - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight); @@ -545,7 +543,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, bsdf->extra = NULL; if (data_node.y == SVM_STACK_INVALID) { - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->alpha_x = roughness; bsdf->alpha_y = roughness; } @@ -581,8 +579,8 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, bsdf->extra = (ccl_private MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); if (bsdf->extra) { - bsdf->extra->color = stack_load_float3(stack, data_node.w); - bsdf->extra->cspec0 = make_float3(0.0f, 0.0f, 0.0f); + bsdf->extra->color = rgb_to_spectrum(stack_load_float3(stack, data_node.w)); + bsdf->extra->cspec0 = zero_spectrum(); bsdf->extra->clearcoat = 0.0f; sd->flag |= bsdf_microfacet_multi_ggx_setup(bsdf); } @@ -600,13 +598,13 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (!kernel_data.integrator.caustics_refractive && (path_flag & PATH_RAY_DIFFUSE)) break; #endif - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight); if (bsdf) { bsdf->N = N; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->extra = NULL; float eta = fmaxf(param2, 1e-5f); @@ -644,7 +642,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, break; } #endif - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; /* index of refraction */ float eta = fmaxf(param2, 1e-5f); @@ -665,7 +663,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (bsdf) { bsdf->N = N; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->extra = NULL; svm_node_glass_setup(sd, bsdf, type, eta, roughness, false); } @@ -683,7 +681,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (bsdf) { bsdf->N = N; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); bsdf->extra = NULL; svm_node_glass_setup(sd, bsdf, type, eta, roughness, true); } @@ -697,7 +695,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, !kernel_data.integrator.caustics_refractive && (path_flag & PATH_RAY_DIFFUSE)) break; #endif - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight); if (!bsdf) { @@ -712,7 +710,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, bsdf->N = N; bsdf->extra = extra; - bsdf->T = make_float3(0.0f, 0.0f, 0.0f); + bsdf->T = zero_float3(); float roughness = sqr(param1); bsdf->alpha_x = roughness; @@ -721,8 +719,8 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, bsdf->ior = (sd->flag & SD_BACKFACING) ? 1.0f / eta : eta; kernel_assert(stack_valid(data_node.z)); - bsdf->extra->color = stack_load_float3(stack, data_node.z); - bsdf->extra->cspec0 = make_float3(0.0f, 0.0f, 0.0f); + bsdf->extra->color = rgb_to_spectrum(stack_load_float3(stack, data_node.z)); + bsdf->extra->cspec0 = zero_spectrum(); bsdf->extra->clearcoat = 0.0f; /* setup bsdf */ @@ -730,7 +728,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, break; } case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID: { - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private VelvetBsdf *bsdf = (ccl_private VelvetBsdf *)bsdf_alloc( sd, sizeof(VelvetBsdf), weight); @@ -749,7 +747,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, ATTR_FALLTHROUGH; #endif case CLOSURE_BSDF_DIFFUSE_TOON_ID: { - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private ToonBsdf *bsdf = (ccl_private ToonBsdf *)bsdf_alloc( sd, sizeof(ToonBsdf), weight); @@ -771,7 +769,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, uint4 data_node3 = read_node(kg, &offset); uint4 data_node4 = read_node(kg, &offset); - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; uint offset_ofs, ior_ofs, color_ofs, parametrization; svm_unpack_node_uchar4(data_node.y, &offset_ofs, &ior_ofs, &color_ofs, ¶metrization); @@ -829,7 +827,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, switch (parametrization) { case NODE_PRINCIPLED_HAIR_DIRECT_ABSORPTION: { float3 absorption_coefficient = stack_load_float3(stack, absorption_coefficient_ofs); - bsdf->sigma = absorption_coefficient; + bsdf->sigma = rgb_to_spectrum(absorption_coefficient); break; } case NODE_PRINCIPLED_HAIR_PIGMENT_CONCENTRATION: { @@ -849,20 +847,21 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, /* Benedikt Bitterli's melanin ratio remapping. */ float eumelanin = melanin * (1.0f - melanin_redness); float pheomelanin = melanin * melanin_redness; - float3 melanin_sigma = bsdf_principled_hair_sigma_from_concentration(eumelanin, - pheomelanin); + Spectrum melanin_sigma = bsdf_principled_hair_sigma_from_concentration(eumelanin, + pheomelanin); /* Optional tint. */ float3 tint = stack_load_float3(stack, tint_ofs); - float3 tint_sigma = bsdf_principled_hair_sigma_from_reflectance(tint, - radial_roughness); + Spectrum tint_sigma = bsdf_principled_hair_sigma_from_reflectance( + rgb_to_spectrum(tint), radial_roughness); bsdf->sigma = melanin_sigma + tint_sigma; break; } case NODE_PRINCIPLED_HAIR_REFLECTANCE: { float3 color = stack_load_float3(stack, color_ofs); - bsdf->sigma = bsdf_principled_hair_sigma_from_reflectance(color, radial_roughness); + bsdf->sigma = bsdf_principled_hair_sigma_from_reflectance(rgb_to_spectrum(color), + radial_roughness); break; } default: { @@ -879,7 +878,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, } case CLOSURE_BSDF_HAIR_REFLECTION_ID: case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: { - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private HairBsdf *bsdf = (ccl_private HairBsdf *)bsdf_alloc( sd, sizeof(HairBsdf), weight); @@ -916,7 +915,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, case CLOSURE_BSSRDF_BURLEY_ID: case CLOSURE_BSSRDF_RANDOM_WALK_ID: case CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID: { - float3 weight = sd->svm_closure_weight * mix_weight; + Spectrum weight = sd->svm_closure_weight * mix_weight; ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, weight); if (bssrdf) { @@ -926,7 +925,7 @@ ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, if (path_flag & PATH_RAY_DIFFUSE_ANCESTOR) param1 = 0.0f; - bssrdf->radius = stack_load_float3(stack, data_node.z) * param1; + bssrdf->radius = rgb_to_spectrum(stack_load_float3(stack, data_node.z) * param1); bssrdf->albedo = sd->svm_closure_weight; bssrdf->N = N; bssrdf->roughness = FLT_MAX; @@ -976,10 +975,10 @@ ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg, density = mix_weight * fmaxf(density, 0.0f); /* Compute scattering coefficient. */ - float3 weight = sd->svm_closure_weight; + Spectrum weight = sd->svm_closure_weight; if (type == CLOSURE_VOLUME_ABSORPTION_ID) { - weight = make_float3(1.0f, 1.0f, 1.0f) - weight; + weight = one_spectrum() - weight; } weight *= density; @@ -1047,11 +1046,11 @@ ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, if (density > CLOSURE_WEIGHT_CUTOFF) { /* Compute scattering color. */ - float3 color = sd->svm_closure_weight; + Spectrum color = sd->svm_closure_weight; const AttributeDescriptor attr_color = find_attribute(kg, sd, attr_node.y); if (attr_color.offset != ATTR_STD_NOT_FOUND) { - color *= primitive_volume_attribute_float3(kg, sd, attr_color); + color *= rgb_to_spectrum(primitive_volume_attribute_float3(kg, sd, attr_color)); } /* Add closure for volume scattering. */ @@ -1066,10 +1065,13 @@ ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, } /* Add extinction weight. */ - float3 zero = make_float3(0.0f, 0.0f, 0.0f); - float3 one = make_float3(1.0f, 1.0f, 1.0f); - float3 absorption_color = max(sqrt(stack_load_float3(stack, absorption_color_offset)), zero); - float3 absorption = max(one - color, zero) * max(one - absorption_color, zero); + float3 absorption_color = max(sqrt(stack_load_float3(stack, absorption_color_offset)), + zero_float3()); + + Spectrum zero = zero_spectrum(); + Spectrum one = one_spectrum(); + Spectrum absorption = max(one - color, zero) * + max(one - rgb_to_spectrum(absorption_color), zero); volume_extinction_setup(sd, (color + absorption) * density); } @@ -1089,7 +1091,7 @@ ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, if (emission > CLOSURE_WEIGHT_CUTOFF) { float3 emission_color = stack_load_float3(stack, emission_color_offset); - emission_setup(sd, emission * emission_color); + emission_setup(sd, rgb_to_spectrum(emission * emission_color)); } if (blackbody > CLOSURE_WEIGHT_CUTOFF) { @@ -1113,7 +1115,7 @@ ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, float3 blackbody_tint = stack_load_float3(stack, node.w); float3 bb = blackbody_tint * intensity * rec709_to_rgb(kg, svm_math_blackbody_color_rec709(T)); - emission_setup(sd, bb); + emission_setup(sd, rgb_to_spectrum(bb)); } } #endif @@ -1125,7 +1127,7 @@ ccl_device_noinline void svm_node_closure_emission(ccl_private ShaderData *sd, uint4 node) { uint mix_weight_offset = node.y; - float3 weight = sd->svm_closure_weight; + Spectrum weight = sd->svm_closure_weight; if (stack_valid(mix_weight_offset)) { float mix_weight = stack_load_float(stack, mix_weight_offset); @@ -1144,7 +1146,7 @@ ccl_device_noinline void svm_node_closure_background(ccl_private ShaderData *sd, uint4 node) { uint mix_weight_offset = node.y; - float3 weight = sd->svm_closure_weight; + Spectrum weight = sd->svm_closure_weight; if (stack_valid(mix_weight_offset)) { float mix_weight = stack_load_float(stack, mix_weight_offset); @@ -1181,14 +1183,15 @@ ccl_device_noinline void svm_node_closure_holdout(ccl_private ShaderData *sd, /* Closure Nodes */ -ccl_device_inline void svm_node_closure_store_weight(ccl_private ShaderData *sd, float3 weight) +ccl_device_inline void svm_node_closure_store_weight(ccl_private ShaderData *sd, Spectrum weight) { sd->svm_closure_weight = weight; } ccl_device void svm_node_closure_set_weight(ccl_private ShaderData *sd, uint r, uint g, uint b) { - float3 weight = make_float3(__uint_as_float(r), __uint_as_float(g), __uint_as_float(b)); + Spectrum weight = rgb_to_spectrum( + make_float3(__uint_as_float(r), __uint_as_float(g), __uint_as_float(b))); svm_node_closure_store_weight(sd, weight); } @@ -1196,7 +1199,7 @@ ccl_device void svm_node_closure_weight(ccl_private ShaderData *sd, ccl_private float *stack, uint weight_offset) { - float3 weight = stack_load_float3(stack, weight_offset); + Spectrum weight = rgb_to_spectrum(stack_load_float3(stack, weight_offset)); svm_node_closure_store_weight(sd, weight); } @@ -1209,7 +1212,7 @@ ccl_device_noinline void svm_node_emission_weight(KernelGlobals kg, uint strength_offset = node.z; float strength = stack_load_float(stack, strength_offset); - float3 weight = stack_load_float3(stack, color_offset) * strength; + Spectrum weight = rgb_to_spectrum(stack_load_float3(stack, color_offset)) * strength; svm_node_closure_store_weight(sd, weight); } diff --git a/intern/cycles/kernel/svm/displace.h b/intern/cycles/kernel/svm/displace.h index 128023263fd..230f8c73820 100644 --- a/intern/cycles/kernel/svm/displace.h +++ b/intern/cycles/kernel/svm/displace.h @@ -24,18 +24,17 @@ ccl_device_noinline void svm_node_set_bump(KernelGlobals kg, float3 normal_in = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; - float3 dPdx = sd->dP.dx; - float3 dPdy = sd->dP.dy; + differential3 dP = differential_from_compact(sd->Ng, sd->dP); if (use_object_space) { object_inverse_normal_transform(kg, sd, &normal_in); - object_inverse_dir_transform(kg, sd, &dPdx); - object_inverse_dir_transform(kg, sd, &dPdy); + object_inverse_dir_transform(kg, sd, &dP.dx); + object_inverse_dir_transform(kg, sd, &dP.dy); } /* get surface tangents from normal */ - float3 Rx = cross(dPdy, normal_in); - float3 Ry = cross(normal_in, dPdx); + float3 Rx = cross(dP.dy, normal_in); + float3 Ry = cross(normal_in, dP.dx); /* get bump values */ uint c_offset, x_offset, y_offset, strength_offset; @@ -46,7 +45,7 @@ ccl_device_noinline void svm_node_set_bump(KernelGlobals kg, float h_y = stack_load_float(stack, y_offset); /* compute surface gradient and determinant */ - float det = dot(dPdx, Rx); + float det = dot(dP.dx, Rx); float3 surfgrad = (h_x - h_c) * Rx + (h_y - h_c) * Ry; float absdet = fabsf(det); diff --git a/intern/cycles/kernel/svm/geometry.h b/intern/cycles/kernel/svm/geometry.h index bbefdcfa755..cbd87d84409 100644 --- a/intern/cycles/kernel/svm/geometry.h +++ b/intern/cycles/kernel/svm/geometry.h @@ -54,7 +54,7 @@ ccl_device_noinline void svm_node_geometry_bump_dx(KernelGlobals kg, switch (type) { case NODE_GEOM_P: - data = sd->P + sd->dP.dx; + data = svm_node_bump_P_dx(sd); break; case NODE_GEOM_uv: data = make_float3(1.0f - sd->u - sd->du.dx - sd->v - sd->dv.dx, sd->u + sd->du.dx, 0.0f); @@ -81,7 +81,7 @@ ccl_device_noinline void svm_node_geometry_bump_dy(KernelGlobals kg, switch (type) { case NODE_GEOM_P: - data = sd->P + sd->dP.dy; + data = svm_node_bump_P_dy(sd); break; case NODE_GEOM_uv: data = make_float3(1.0f - sd->u - sd->du.dy - sd->v - sd->dv.dy, sd->u + sd->du.dy, 0.0f); diff --git a/intern/cycles/kernel/svm/tex_coord.h b/intern/cycles/kernel/svm/tex_coord.h index 2a0130e11d4..8154c542e6f 100644 --- a/intern/cycles/kernel/svm/tex_coord.h +++ b/intern/cycles/kernel/svm/tex_coord.h @@ -106,7 +106,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, switch (type) { case NODE_TEXCO_OBJECT: { - data = sd->P + sd->dP.dx; + data = svm_node_bump_P_dx(sd); if (node.w == 0) { if (sd->object != OBJECT_NONE) { object_inverse_position_transform(kg, sd, &data); @@ -130,9 +130,9 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, Transform tfm = kernel_data.cam.worldtocamera; if (sd->object != OBJECT_NONE) - data = transform_point(&tfm, sd->P + sd->dP.dx); + data = transform_point(&tfm, svm_node_bump_P_dx(sd)); else - data = transform_point(&tfm, sd->P + sd->dP.dx + camera_position(kg)); + data = transform_point(&tfm, svm_node_bump_P_dx(sd) + camera_position(kg)); break; } case NODE_TEXCO_WINDOW: { @@ -140,7 +140,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) data = camera_world_to_ndc(kg, sd, sd->ray_P); else - data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx); + data = camera_world_to_ndc(kg, sd, svm_node_bump_P_dx(sd)); data.z = 0.0f; break; } @@ -160,7 +160,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, break; } case NODE_TEXCO_VOLUME_GENERATED: { - data = sd->P + sd->dP.dx; + data = svm_node_bump_P_dx(sd); # ifdef __VOLUME__ if (sd->object != OBJECT_NONE) @@ -191,7 +191,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, switch (type) { case NODE_TEXCO_OBJECT: { - data = sd->P + sd->dP.dy; + data = svm_node_bump_P_dy(sd); if (node.w == 0) { if (sd->object != OBJECT_NONE) { object_inverse_position_transform(kg, sd, &data); @@ -215,9 +215,9 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, Transform tfm = kernel_data.cam.worldtocamera; if (sd->object != OBJECT_NONE) - data = transform_point(&tfm, sd->P + sd->dP.dy); + data = transform_point(&tfm, svm_node_bump_P_dy(sd)); else - data = transform_point(&tfm, sd->P + sd->dP.dy + camera_position(kg)); + data = transform_point(&tfm, svm_node_bump_P_dy(sd) + camera_position(kg)); break; } case NODE_TEXCO_WINDOW: { @@ -225,7 +225,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) data = camera_world_to_ndc(kg, sd, sd->ray_P); else - data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy); + data = camera_world_to_ndc(kg, sd, svm_node_bump_P_dy(sd)); data.z = 0.0f; break; } @@ -245,7 +245,7 @@ ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, break; } case NODE_TEXCO_VOLUME_GENERATED: { - data = sd->P + sd->dP.dy; + data = svm_node_bump_P_dy(sd); # ifdef __VOLUME__ if (sd->object != OBJECT_NONE) diff --git a/intern/cycles/kernel/svm/types.h b/intern/cycles/kernel/svm/types.h index 12d0ec141e6..98dfe6a4375 100644 --- a/intern/cycles/kernel/svm/types.h +++ b/intern/cycles/kernel/svm/types.h @@ -12,7 +12,7 @@ CCL_NAMESPACE_BEGIN /* SVM stack offsets with this value indicate that it's not on the stack */ #define SVM_STACK_INVALID 255 -#define SVM_BUMP_EVAL_STATE_SIZE 9 +#define SVM_BUMP_EVAL_STATE_SIZE 4 /* Nodes */ diff --git a/intern/cycles/kernel/svm/wireframe.h b/intern/cycles/kernel/svm/wireframe.h index e5fe08e5d04..91fadf4cfc4 100644 --- a/intern/cycles/kernel/svm/wireframe.h +++ b/intern/cycles/kernel/svm/wireframe.h @@ -14,6 +14,7 @@ CCL_NAMESPACE_BEGIN ccl_device_inline float wireframe(KernelGlobals kg, ccl_private ShaderData *sd, + const differential3 dP, float size, int pixel_size, ccl_private float3 *P) @@ -46,8 +47,8 @@ ccl_device_inline float wireframe(KernelGlobals kg, if (pixel_size) { // Project the derivatives of P to the viewing plane defined // by I so we have a measure of how big is a pixel at this point - float pixelwidth_x = len(sd->dP.dx - dot(sd->dP.dx, sd->I) * sd->I); - float pixelwidth_y = len(sd->dP.dy - dot(sd->dP.dy, sd->I) * sd->I); + float pixelwidth_x = len(dP.dx - dot(dP.dx, sd->I) * sd->I); + float pixelwidth_y = len(dP.dy - dot(dP.dy, sd->I) * sd->I); // Take the average of both axis' length pixelwidth = (pixelwidth_x + pixelwidth_y) * 0.5f; } @@ -86,16 +87,17 @@ ccl_device_noinline void svm_node_wireframe(KernelGlobals kg, int pixel_size = (int)use_pixel_size; /* Calculate wireframe */ - float f = wireframe(kg, sd, size, pixel_size, &sd->P); + const differential3 dP = differential_from_compact(sd->Ng, sd->dP); + float f = wireframe(kg, sd, dP, size, pixel_size, &sd->P); /* TODO(sergey): Think of faster way to calculate derivatives. */ if (bump_offset == NODE_BUMP_OFFSET_DX) { - float3 Px = sd->P - sd->dP.dx; - f += (f - wireframe(kg, sd, size, pixel_size, &Px)) / len(sd->dP.dx); + float3 Px = sd->P - dP.dx; + f += (f - wireframe(kg, sd, dP, size, pixel_size, &Px)) / len(dP.dx); } else if (bump_offset == NODE_BUMP_OFFSET_DY) { - float3 Py = sd->P - sd->dP.dy; - f += (f - wireframe(kg, sd, size, pixel_size, &Py)) / len(sd->dP.dy); + float3 Py = sd->P - dP.dy; + f += (f - wireframe(kg, sd, dP, size, pixel_size, &Py)) / len(dP.dy); } if (stack_valid(out_fac)) diff --git a/intern/cycles/kernel/types.h b/intern/cycles/kernel/types.h index 7762c95275e..59ea6c64be7 100644 --- a/intern/cycles/kernel/types.h +++ b/intern/cycles/kernel/types.h @@ -413,9 +413,9 @@ typedef enum CryptomatteType { } CryptomatteType; typedef struct BsdfEval { - float3 diffuse; - float3 glossy; - float3 sum; + Spectrum diffuse; + Spectrum glossy; + Spectrum sum; } BsdfEval; /* Closure Filter */ @@ -709,7 +709,7 @@ typedef struct AttributeMap { * padded to be 16 bytes, while it's only 12 bytes on the GPU. */ #define SHADER_CLOSURE_BASE \ - float3 weight; \ + Spectrum weight; \ ClosureType type; \ float sample_weight; \ float3 N @@ -718,10 +718,9 @@ typedef struct ccl_align(16) ShaderClosure { SHADER_CLOSURE_BASE; -#ifndef __KERNEL_GPU__ - float pad[2]; -#endif - float data[10]; + /* Extra space for closures to store data, somewhat arbitrary but closures + * assert that their size fits. */ + char pad[sizeof(Spectrum) * 2 + sizeof(float) * 4]; } ShaderClosure; @@ -874,10 +873,10 @@ typedef struct ccl_align(16) ShaderData float ray_length; #ifdef __RAY_DIFFERENTIALS__ - /* differential of P. these are orthogonal to Ng, not N */ - differential3 dP; - /* differential of I */ - differential3 dI; + /* Radius of differential of P. */ + float dP; + /* Radius of differential of I. */ + float dI; /* differential of u, v */ differential du; differential dv; @@ -912,12 +911,12 @@ typedef struct ccl_align(16) ShaderData /* Closure data, we store a fixed array of closures */ int num_closure; int num_closure_left; - float3 svm_closure_weight; + Spectrum svm_closure_weight; /* Closure weights summed directly, so we can evaluate * emission and shadow transparency with MAX_CLOSURE 0. */ - float3 closure_emission_background; - float3 closure_transparent_extinction; + Spectrum closure_emission_background; + Spectrum closure_transparent_extinction; /* At the end so we can adjust size in ShaderDataTinyStorage. */ struct ShaderClosure closure[MAX_CLOSURE]; @@ -948,7 +947,7 @@ ShaderDataCausticsStorage; * Used for decoupled direct/indirect light closure storage. */ typedef struct ShaderVolumeClosure { - float3 weight; + Spectrum weight; float sample_weight; float g; } ShaderVolumeClosure; diff --git a/intern/cycles/kernel/util/color.h b/intern/cycles/kernel/util/color.h index c85ef262d88..4983b9048d4 100644 --- a/intern/cycles/kernel/util/color.h +++ b/intern/cycles/kernel/util/color.h @@ -33,4 +33,19 @@ ccl_device float linear_rgb_to_gray(KernelGlobals kg, float3 c) return dot(c, float4_to_float3(kernel_data.film.rgb_to_y)); } +ccl_device_inline Spectrum rgb_to_spectrum(float3 rgb) +{ + return rgb; +} + +ccl_device_inline float3 spectrum_to_rgb(Spectrum s) +{ + return s; +} + +ccl_device float spectrum_to_gray(KernelGlobals kg, Spectrum c) +{ + return linear_rgb_to_gray(kg, spectrum_to_rgb(c)); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/util/differential.h b/intern/cycles/kernel/util/differential.h index 3682e91ea66..aad9bb6bb22 100644 --- a/intern/cycles/kernel/util/differential.h +++ b/intern/cycles/kernel/util/differential.h @@ -101,53 +101,59 @@ ccl_device differential3 differential3_zero() return d; } -/* Compact ray differentials that are just a scale to reduce memory usage and - * access cost in GPU. +/* Compact ray differentials that are just a radius to reduce memory usage and access cost + * on GPUs, basically cone tracing. * - * See above for more accurate reference implementations. - * - * TODO: also store the more compact version in ShaderData and recompute where - * needed? */ + * See above for more accurate reference implementations of ray differentials. */ ccl_device_forceinline float differential_zero_compact() { return 0.0f; } -ccl_device_forceinline float differential_make_compact(const differential3 D) +ccl_device_forceinline float differential_make_compact(const float dD) { - return 0.5f * (len(D.dx) + len(D.dy)); + return dD; } -ccl_device_forceinline void differential_transfer_compact(ccl_private differential3 *surface_dP, - const float ray_dP, - const float3 /* ray_D */, - const float ray_dD, - const float3 surface_Ng, - const float ray_t) +ccl_device_forceinline float differential_make_compact(const differential3 dD) { - /* ray differential transfer through homogeneous medium, to - * compute dPdx/dy at a shading point from the incoming ray */ - float scale = ray_dP + ray_t * ray_dD; + return 0.5f * (len(dD.dx) + len(dD.dy)); +} - float3 dx, dy; - make_orthonormals(surface_Ng, &dx, &dy); - surface_dP->dx = dx * scale; - surface_dP->dy = dy * scale; +ccl_device_forceinline float differential_incoming_compact(const float dD) +{ + return dD; } -ccl_device_forceinline void differential_incoming_compact(ccl_private differential3 *dI, - const float3 D, - const float dD) +ccl_device_forceinline float differential_transfer_compact(const float ray_dP, + const float3 /* ray_D */, + const float ray_dD, + const float ray_t) { - /* compute dIdx/dy at a shading point, we just need to negate the - * differential of the ray direction */ + return ray_dP + ray_t * ray_dD; +} +ccl_device_forceinline differential3 differential_from_compact(const float3 D, const float dD) +{ float3 dx, dy; make_orthonormals(D, &dx, &dy); - dI->dx = dD * dx; - dI->dy = dD * dy; + differential3 d; + d.dx = dD * dx; + d.dy = dD * dy; + return d; +} + +ccl_device void differential_dudv_compact(ccl_private differential *du, + ccl_private differential *dv, + float3 dPdu, + float3 dPdv, + float dP, + float3 Ng) +{ + /* TODO: can we speed this up? */ + differential_dudv(du, dv, dPdu, dPdv, differential_from_compact(Ng, dP), Ng); } CCL_NAMESPACE_END diff --git a/intern/cycles/scene/camera.cpp b/intern/cycles/scene/camera.cpp index eec269ab542..d9e574873bd 100644 --- a/intern/cycles/scene/camera.cpp +++ b/intern/cycles/scene/camera.cpp @@ -772,10 +772,7 @@ float Camera::world_to_raster_size(float3 P) #endif /* TODO: would it help to use more accurate differentials here? */ - differential3 dP; - differential_transfer_compact(&dP, ray.dP, ray.D, ray.dD, ray.D, dist); - - return max(len(dP.dx), len(dP.dy)); + return differential_transfer_compact(ray.dP, ray.D, ray.dD, dist); } return res; diff --git a/intern/cycles/scene/image_oiio.cpp b/intern/cycles/scene/image_oiio.cpp index 500e53ed763..8792393e5a1 100644 --- a/intern/cycles/scene/image_oiio.cpp +++ b/intern/cycles/scene/image_oiio.cpp @@ -184,9 +184,8 @@ bool OIIOImageLoader::load_pixels(const ImageMetaData &metadata, ImageSpec config = ImageSpec(); /* Load without automatic OIIO alpha conversion, we do it ourselves. OIIO - * will associate alpha in the the 8bit buffer for PNGs, which leads to too - * much precision loss when we load it as half float to do a colorspace - * transform. */ + * will associate alpha in the 8bit buffer for PNGs, which leads to too + * much precision loss when we load it as half float to do a color-space transform. */ config.attribute("oiio:UnassociatedAlpha", 1); if (!in->open(filepath.string(), spec, config)) { diff --git a/intern/cycles/session/session.cpp b/intern/cycles/session/session.cpp index c94b53535a7..e5df22211e7 100644 --- a/intern/cycles/session/session.cpp +++ b/intern/cycles/session/session.cpp @@ -503,7 +503,9 @@ void Session::do_delayed_reset() if (!params.background) { progress.set_start_time(); } + const double time_limit = params.time_limit * ((double)tile_manager_.get_num_tiles()); progress.set_render_start_time(); + progress.set_time_limit(time_limit); } void Session::reset(const SessionParams &session_params, const BufferParams &buffer_params) @@ -598,7 +600,8 @@ double Session::get_estimated_remaining_time() const progress.get_time(total_time, render_time); double remaining = (1.0 - (double)completed) * (render_time / (double)completed); - const double time_limit = render_scheduler_.get_time_limit(); + const double time_limit = render_scheduler_.get_time_limit() * + ((double)tile_manager_.get_num_tiles()); if (time_limit != 0.0) { remaining = min(remaining, max(time_limit - render_time, 0.0)); } diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index f3fc7739f66..81a7607baab 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -118,6 +118,7 @@ set(SRC_HEADERS types_int3_impl.h types_int4.h types_int4_impl.h + types_spectrum.h types_uchar2.h types_uchar2_impl.h types_uchar3.h @@ -131,8 +132,6 @@ set(SRC_HEADERS types_uint4.h types_uint4_impl.h types_ushort4.h - types_vector3.h - types_vector3_impl.h unique_ptr.h vector.h version.h diff --git a/intern/cycles/util/debug.cpp b/intern/cycles/util/debug.cpp index faa54a92c24..8210e21f951 100644 --- a/intern/cycles/util/debug.cpp +++ b/intern/cycles/util/debug.cpp @@ -13,7 +13,6 @@ CCL_NAMESPACE_BEGIN DebugFlags::CPU::CPU() - : avx2(true), avx(true), sse41(true), sse3(true), sse2(true), bvh_layout(BVH_LAYOUT_AUTO) { reset(); } @@ -41,17 +40,17 @@ void DebugFlags::CPU::reset() bvh_layout = BVH_LAYOUT_AUTO; } -DebugFlags::CUDA::CUDA() : adaptive_compile(false) +DebugFlags::CUDA::CUDA() { reset(); } -DebugFlags::HIP::HIP() : adaptive_compile(false) +DebugFlags::HIP::HIP() { reset(); } -DebugFlags::Metal::Metal() : adaptive_compile(false) +DebugFlags::Metal::Metal() { reset(); } @@ -84,14 +83,13 @@ void DebugFlags::OptiX::reset() use_debug = false; } -DebugFlags::DebugFlags() : viewport_static_bvh(false), running_inside_blender(false) +DebugFlags::DebugFlags() { /* Nothing for now. */ } void DebugFlags::reset() { - viewport_static_bvh = false; cpu.reset(); cuda.reset(); optix.reset(); diff --git a/intern/cycles/util/debug.h b/intern/cycles/util/debug.h index 3565fdea17f..ab200649f59 100644 --- a/intern/cycles/util/debug.h +++ b/intern/cycles/util/debug.h @@ -17,11 +17,6 @@ CCL_NAMESPACE_BEGIN */ class DebugFlags { public: - /* Use static BVH in viewport, to match final render exactly. */ - bool viewport_static_bvh; - - bool running_inside_blender; - /* Descriptor of CPU feature-set to be used. */ struct CPU { CPU(); @@ -30,11 +25,11 @@ class DebugFlags { void reset(); /* Flags describing which instructions sets are allowed for use. */ - bool avx2; - bool avx; - bool sse41; - bool sse3; - bool sse2; + bool avx2 = true; + bool avx = true; + bool sse41 = true; + bool sse3 = true; + bool sse2 = true; /* Check functions to see whether instructions up to the given one * are allowed for use. @@ -65,7 +60,7 @@ class DebugFlags { * By default the fastest will be used. For debugging the BVH used by other * CPUs and GPUs can be selected here instead. */ - BVHLayout bvh_layout; + BVHLayout bvh_layout = BVH_LAYOUT_AUTO; }; /* Descriptor of CUDA feature-set to be used. */ @@ -77,7 +72,7 @@ class DebugFlags { /* Whether adaptive feature based runtime compile is enabled or not. * Requires the CUDA Toolkit and only works on Linux at the moment. */ - bool adaptive_compile; + bool adaptive_compile = false; }; /* Descriptor of HIP feature-set to be used. */ @@ -88,7 +83,7 @@ class DebugFlags { void reset(); /* Whether adaptive feature based runtime compile is enabled or not. */ - bool adaptive_compile; + bool adaptive_compile = false; }; /* Descriptor of OptiX feature-set to be used. */ @@ -100,7 +95,7 @@ class DebugFlags { /* Load OptiX module with debug capabilities. Will lower logging verbosity level, enable * validations, and lower optimization level. */ - bool use_debug; + bool use_debug = false; }; /* Descriptor of Metal feature-set to be used. */ @@ -111,7 +106,7 @@ class DebugFlags { void reset(); /* Whether adaptive feature based runtime compile is enabled or not. */ - bool adaptive_compile; + bool adaptive_compile = false; }; /* Get instance of debug flags registry. */ @@ -142,15 +137,9 @@ class DebugFlags { private: DebugFlags(); -#if (__cplusplus > 199711L) public: explicit DebugFlags(DebugFlags const & /*other*/) = delete; void operator=(DebugFlags const & /*other*/) = delete; -#else - private: - explicit DebugFlags(DebugFlags const & /*other*/); - void operator=(DebugFlags const & /*other*/); -#endif }; typedef DebugFlags &DebugFlagsRef; diff --git a/intern/cycles/util/defines.h b/intern/cycles/util/defines.h index d0df1a221fc..c7118ca09c9 100644 --- a/intern/cycles/util/defines.h +++ b/intern/cycles/util/defines.h @@ -136,4 +136,7 @@ template<typename T> static inline T decltype_helper(T x) # define util_assert(statement) #endif +#define CONCAT_HELPER(a, ...) a##__VA_ARGS__ +#define CONCAT(a, ...) CONCAT_HELPER(a, __VA_ARGS__) + #endif /* __UTIL_DEFINES_H__ */ diff --git a/intern/cycles/util/math.h b/intern/cycles/util/math.h index f6400cb879f..0585dcc8ad5 100644 --- a/intern/cycles/util/math.h +++ b/intern/cycles/util/math.h @@ -595,26 +595,26 @@ ccl_device_inline void make_orthonormals(const float3 N, /* Color division */ -ccl_device_inline float3 safe_invert_color(float3 a) +ccl_device_inline Spectrum safe_invert_color(Spectrum a) { - float x, y, z; - - x = (a.x != 0.0f) ? 1.0f / a.x : 0.0f; - y = (a.y != 0.0f) ? 1.0f / a.y : 0.0f; - z = (a.z != 0.0f) ? 1.0f / a.z : 0.0f; + FOREACH_SPECTRUM_CHANNEL (i) { + GET_SPECTRUM_CHANNEL(a, i) = (GET_SPECTRUM_CHANNEL(a, i) != 0.0f) ? + 1.0f / GET_SPECTRUM_CHANNEL(a, i) : + 0.0f; + } - return make_float3(x, y, z); + return a; } -ccl_device_inline float3 safe_divide_color(float3 a, float3 b) +ccl_device_inline Spectrum safe_divide_color(Spectrum a, Spectrum b) { - float x, y, z; - - x = (b.x != 0.0f) ? a.x / b.x : 0.0f; - y = (b.y != 0.0f) ? a.y / b.y : 0.0f; - z = (b.z != 0.0f) ? a.z / b.z : 0.0f; + FOREACH_SPECTRUM_CHANNEL (i) { + GET_SPECTRUM_CHANNEL(a, i) = (GET_SPECTRUM_CHANNEL(b, i) != 0.0f) ? + GET_SPECTRUM_CHANNEL(a, i) / GET_SPECTRUM_CHANNEL(b, i) : + 0.0f; + } - return make_float3(x, y, z); + return a; } ccl_device_inline float3 safe_divide_even_color(float3 a, float3 b) diff --git a/intern/cycles/util/progress.h b/intern/cycles/util/progress.h index 37eafd57491..586979d2021 100644 --- a/intern/cycles/util/progress.h +++ b/intern/cycles/util/progress.h @@ -28,6 +28,7 @@ class Progress { denoised_tiles = 0; start_time = time_dt(); render_start_time = time_dt(); + time_limit = 0.0; end_time = 0.0; status = "Initializing"; substatus = ""; @@ -68,6 +69,7 @@ class Progress { denoised_tiles = 0; start_time = time_dt(); render_start_time = time_dt(); + time_limit = 0.0; end_time = 0.0; status = "Initializing"; substatus = ""; @@ -145,6 +147,13 @@ class Progress { render_start_time = time_dt(); } + void set_time_limit(double time_limit_) + { + thread_scoped_lock lock(progress_mutex); + + time_limit = time_limit_; + } + void add_skip_time(const scoped_timer &start_timer, bool only_render) { double skip_time = time_dt() - start_timer.get_start(); @@ -191,8 +200,13 @@ class Progress { { thread_scoped_lock lock(progress_mutex); - if (total_pixel_samples > 0) { - return ((double)pixel_samples) / (double)total_pixel_samples; + if (pixel_samples > 0) { + double progress_percent = (double)pixel_samples / (double)total_pixel_samples; + if (time_limit != 0.0) { + double time_since_render_start = time_dt() - render_start_time; + progress_percent = max(progress_percent, time_since_render_start / time_limit); + } + return min(1.0, progress_percent); } return 0.0; } @@ -335,7 +349,7 @@ class Progress { * in which case the current_tile_sample is displayed. */ int rendered_tiles, denoised_tiles; - double start_time, render_start_time; + double start_time, render_start_time, time_limit; /* End time written when render is done, so it doesn't keep increasing on redraws. */ double end_time; diff --git a/intern/cycles/util/types.h b/intern/cycles/util/types.h index 031c2f7c4c1..1ab6f76f9bc 100644 --- a/intern/cycles/util/types.h +++ b/intern/cycles/util/types.h @@ -12,6 +12,7 @@ #if !defined(__KERNEL_GPU__) # include <stdint.h> +# include <stdio.h> #endif #include "util/defines.h" @@ -70,6 +71,24 @@ ccl_device_inline bool is_power_of_two(size_t x) CCL_NAMESPACE_END +/* Device side printf only tested on CUDA, may work on more GPU devices. */ +#if !defined(__KERNEL_GPU__) || defined(__KERNEL_CUDA__) +# define __KERNEL_PRINTF__ +#endif + +ccl_device_inline void print_float(ccl_private const char *label, const float a) +{ +#ifdef __KERNEL_PRINTF__ + printf("%s: %.8f\n", label, (double)a); +#endif +} + +/* Most GPU APIs matching native vector types, so we only need to implement them for + * CPU and oneAPI. */ +#if defined(__KERNEL_GPU__) && !defined(__KERNEL_ONEAPI__) +# define __KERNEL_NATIVE_VECTOR_TYPES__ +#endif + /* Vectorized types declaration. */ #include "util/types_uchar2.h" #include "util/types_uchar3.h" @@ -90,7 +109,7 @@ CCL_NAMESPACE_END #include "util/types_float4.h" #include "util/types_float8.h" -#include "util/types_vector3.h" +#include "util/types_spectrum.h" /* Vectorized types implementation. */ #include "util/types_uchar2_impl.h" @@ -110,8 +129,6 @@ CCL_NAMESPACE_END #include "util/types_float4_impl.h" #include "util/types_float8_impl.h" -#include "util/types_vector3_impl.h" - /* SSE types. */ #ifndef __KERNEL_GPU__ # include "util/sseb.h" diff --git a/intern/cycles/util/types_float2.h b/intern/cycles/util/types_float2.h index 07b9ec0986b..ea510ef832c 100644 --- a/intern/cycles/util/types_float2.h +++ b/intern/cycles/util/types_float2.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT2_H__ -#define __UTIL_TYPES_FLOAT2_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,18 +9,19 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct float2 { float x, y; +# ifndef __KERNEL_GPU__ __forceinline float operator[](int i) const; __forceinline float &operator[](int i); +# endif }; ccl_device_inline float2 make_float2(float x, float y); -ccl_device_inline void print_float2(const char *label, const float2 &a); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ -CCL_NAMESPACE_END +ccl_device_inline void print_float2(ccl_private const char *label, const float2 a); -#endif /* __UTIL_TYPES_FLOAT2_H__ */ +CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_float2_impl.h b/intern/cycles/util/types_float2_impl.h index 45fc90c52bd..7ba7dee2e3a 100644 --- a/intern/cycles/util/types_float2_impl.h +++ b/intern/cycles/util/types_float2_impl.h @@ -1,20 +1,16 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT2_IMPL_H__ -#define __UTIL_TYPES_FLOAT2_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ __forceinline float float2::operator[](int i) const { util_assert(i >= 0); @@ -28,19 +24,20 @@ __forceinline float &float2::operator[](int i) util_assert(i < 2); return *(&x + i); } +# endif ccl_device_inline float2 make_float2(float x, float y) { float2 a = {x, y}; return a; } +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ -ccl_device_inline void print_float2(const char *label, const float2 &a) +ccl_device_inline void print_float2(ccl_private const char *label, const float2 a) { +#ifdef __KERNEL_PRINTF__ printf("%s: %.8f %.8f\n", label, (double)a.x, (double)a.y); +#endif } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT2_IMPL_H__ */ diff --git a/intern/cycles/util/types_float3.h b/intern/cycles/util/types_float3.h index c7900acaa69..87c6b1d3654 100644 --- a/intern/cycles/util/types_float3.h +++ b/intern/cycles/util/types_float3.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT3_H__ -#define __UTIL_TYPES_FLOAT3_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,28 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct ccl_try_align(16) float3 { -# ifdef __KERNEL_SSE__ +# ifdef __KERNEL_GPU__ + /* Compact structure for GPU. */ + float x, y, z; +# else + /* SIMD aligned structure for CPU. */ +# ifdef __KERNEL_SSE__ union { __m128 m128; struct { float x, y, z, w; }; }; +# else + float x, y, z, w; +# endif +# endif +# ifdef __KERNEL_SSE__ + /* Convenient constructors and operators for SIMD, otherwise default is enough. */ __forceinline float3(); __forceinline float3(const float3 &a); __forceinline explicit float3(const __m128 &a); @@ -29,18 +39,19 @@ struct ccl_try_align(16) float3 __forceinline operator __m128 &(); __forceinline float3 &operator=(const float3 &a); -# else /* __KERNEL_SSE__ */ - float x, y, z, w; -# endif /* __KERNEL_SSE__ */ +# endif +# ifndef __KERNEL_GPU__ __forceinline float operator[](int i) const; __forceinline float &operator[](int i); +# endif }; -ccl_device_inline float3 make_float3(float f); ccl_device_inline float3 make_float3(float x, float y, float z); -ccl_device_inline void print_float3(const char *label, const float3 &a); -#endif /* !defined(__KERNEL_GPU__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ + +ccl_device_inline float3 make_float3(float f); +ccl_device_inline void print_float3(ccl_private const char *label, const float3 a); /* Smaller float3 for storage. For math operations this must be converted to float3, so that on the * CPU SIMD instructions can be used. */ @@ -78,5 +89,3 @@ struct packed_float3 { static_assert(sizeof(packed_float3) == 12, "packed_float3 expected to be exactly 12 bytes"); CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT3_H__ */ diff --git a/intern/cycles/util/types_float3_impl.h b/intern/cycles/util/types_float3_impl.h index 2e6e864c8ea..da76ab2ab2a 100644 --- a/intern/cycles/util/types_float3_impl.h +++ b/intern/cycles/util/types_float3_impl.h @@ -1,20 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT3_IMPL_H__ -#define __UTIL_TYPES_FLOAT3_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ # ifdef __KERNEL_SSE__ __forceinline float3::float3() { @@ -45,6 +40,7 @@ __forceinline float3 &float3::operator=(const float3 &a) } # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline float float3::operator[](int i) const { util_assert(i >= 0); @@ -58,33 +54,37 @@ __forceinline float &float3::operator[](int i) util_assert(i < 3); return *(&x + i); } +# endif -ccl_device_inline float3 make_float3(float f) +ccl_device_inline float3 make_float3(float x, float y, float z) { -# ifdef __KERNEL_SSE__ - float3 a(_mm_set1_ps(f)); +# if defined(__KERNEL_GPU__) + return {x, y, z}; +# elif defined(__KERNEL_SSE__) + return float3(_mm_set_ps(0.0f, z, y, x)); # else - float3 a = {f, f, f, f}; + return {x, y, z, 0.0f}; # endif - return a; } -ccl_device_inline float3 make_float3(float x, float y, float z) +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ + +ccl_device_inline float3 make_float3(float f) { -# ifdef __KERNEL_SSE__ - float3 a(_mm_set_ps(0.0f, z, y, x)); -# else - float3 a = {x, y, z, 0.0f}; -# endif - return a; +#if defined(__KERNEL_GPU__) + return make_float3(f, f, f); +#elif defined(__KERNEL_SSE__) + return float3(_mm_set1_ps(f)); +#else + return {f, f, f, f}; +#endif } -ccl_device_inline void print_float3(const char *label, const float3 &a) +ccl_device_inline void print_float3(ccl_private const char *label, const float3 a) { +#ifdef __KERNEL_PRINTF__ printf("%s: %.8f %.8f %.8f\n", label, (double)a.x, (double)a.y, (double)a.z); +#endif } -#endif /* !defined(__KERNEL_GPU__) */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT3_IMPL_H__ */ diff --git a/intern/cycles/util/types_float4.h b/intern/cycles/util/types_float4.h index 27453bf39e4..a347cfce9a1 100644 --- a/intern/cycles/util/types_float4.h +++ b/intern/cycles/util/types_float4.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT4_H__ -#define __UTIL_TYPES_FLOAT4_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,7 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct int4; struct ccl_try_align(16) float4 @@ -35,16 +34,17 @@ struct ccl_try_align(16) float4 float x, y, z, w; # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline float operator[](int i) const; __forceinline float &operator[](int i); +# endif }; -ccl_device_inline float4 make_float4(float f); ccl_device_inline float4 make_float4(float x, float y, float z, float w); -ccl_device_inline float4 make_float4(const int4 &i); -ccl_device_inline void print_float4(const char *label, const float4 &a); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ -CCL_NAMESPACE_END +ccl_device_inline float4 make_float4(float f); +ccl_device_inline float4 make_float4(const int4 i); +ccl_device_inline void print_float4(ccl_private const char *label, const float4 a); -#endif /* __UTIL_TYPES_FLOAT4_H__ */ +CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_float4_impl.h b/intern/cycles/util/types_float4_impl.h index d7858f744e3..420d9316926 100644 --- a/intern/cycles/util/types_float4_impl.h +++ b/intern/cycles/util/types_float4_impl.h @@ -1,20 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_FLOAT4_IMPL_H__ -#define __UTIL_TYPES_FLOAT4_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ # ifdef __KERNEL_SSE__ __forceinline float4::float4() { @@ -41,6 +36,7 @@ __forceinline float4 &float4::operator=(const float4 &a) } # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline float float4::operator[](int i) const { util_assert(i >= 0); @@ -54,43 +50,42 @@ __forceinline float &float4::operator[](int i) util_assert(i < 4); return *(&x + i); } +# endif -ccl_device_inline float4 make_float4(float f) +ccl_device_inline float4 make_float4(float x, float y, float z, float w) { # ifdef __KERNEL_SSE__ - float4 a(_mm_set1_ps(f)); + return float4(_mm_set_ps(w, z, y, x)); # else - float4 a = {f, f, f, f}; + return {x, y, z, w}; # endif - return a; } -ccl_device_inline float4 make_float4(float x, float y, float z, float w) +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ + +ccl_device_inline float4 make_float4(float f) { -# ifdef __KERNEL_SSE__ - float4 a(_mm_set_ps(w, z, y, x)); -# else - float4 a = {x, y, z, w}; -# endif - return a; +#ifdef __KERNEL_SSE__ + return float4(_mm_set1_ps(f)); +#else + return make_float4(f, f, f, f); +#endif } -ccl_device_inline float4 make_float4(const int4 &i) +ccl_device_inline float4 make_float4(const int4 i) { -# ifdef __KERNEL_SSE__ - float4 a(_mm_cvtepi32_ps(i.m128)); -# else - float4 a = {(float)i.x, (float)i.y, (float)i.z, (float)i.w}; -# endif - return a; +#ifdef __KERNEL_SSE__ + return float4(_mm_cvtepi32_ps(i.m128)); +#else + return make_float4((float)i.x, (float)i.y, (float)i.z, (float)i.w); +#endif } -ccl_device_inline void print_float4(const char *label, const float4 &a) +ccl_device_inline void print_float4(ccl_private const char *label, const float4 a) { +#ifdef __KERNEL_PRINTF__ printf("%s: %.8f %.8f %.8f %.8f\n", label, (double)a.x, (double)a.y, (double)a.z, (double)a.w); +#endif } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT4_IMPL_H__ */ diff --git a/intern/cycles/util/types_float8.h b/intern/cycles/util/types_float8.h index bb9798932ac..29fd632f08e 100644 --- a/intern/cycles/util/types_float8.h +++ b/intern/cycles/util/types_float8.h @@ -2,8 +2,7 @@ * Original code Copyright 2017, Intel Corporation * Modifications Copyright 2018-2022 Blender Foundation. */ -#ifndef __UTIL_TYPES_FLOAT8_H__ -#define __UTIL_TYPES_FLOAT8_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -12,7 +11,7 @@ CCL_NAMESPACE_BEGIN /* float8 is a reserved type in Metal that has not been implemented. For - * that reason this is named float8_t. */ + * that reason this is named float8_t and not using native vector types. */ #ifdef __KERNEL_GPU__ struct float8_t @@ -52,5 +51,3 @@ ccl_device_inline float8_t make_float8_t(float a, float b, float c, float d, float e, float f, float g, float h); CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT8_H__ */ diff --git a/intern/cycles/util/types_float8_impl.h b/intern/cycles/util/types_float8_impl.h index 2ab464a791b..e8576cdaf70 100644 --- a/intern/cycles/util/types_float8_impl.h +++ b/intern/cycles/util/types_float8_impl.h @@ -2,17 +2,12 @@ * Original code Copyright 2017, Intel Corporation * Modifications Copyright 2018-2022 Blender Foundation. */ -#ifndef __UTIL_TYPES_FLOAT8_IMPL_H__ -#define __UTIL_TYPES_FLOAT8_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN #ifdef __KERNEL_AVX2__ @@ -83,5 +78,3 @@ make_float8_t(float a, float b, float c, float d, float e, float f, float g, flo } CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_FLOAT8_IMPL_H__ */ diff --git a/intern/cycles/util/types_int2.h b/intern/cycles/util/types_int2.h index bf69cddc653..604713dffcd 100644 --- a/intern/cycles/util/types_int2.h +++ b/intern/cycles/util/types_int2.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT2_H__ -#define __UTIL_TYPES_INT2_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct int2 { int x, y; +# ifndef __KERNEL_GPU__ __forceinline int operator[](int i) const; __forceinline int &operator[](int i); +# endif }; ccl_device_inline int2 make_int2(int x, int y); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT2_H__ */ diff --git a/intern/cycles/util/types_int2_impl.h b/intern/cycles/util/types_int2_impl.h index 7bdc77369ee..f48c6f46729 100644 --- a/intern/cycles/util/types_int2_impl.h +++ b/intern/cycles/util/types_int2_impl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT2_IMPL_H__ -#define __UTIL_TYPES_INT2_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ int int2::operator[](int i) const { util_assert(i >= 0); @@ -24,14 +24,13 @@ int &int2::operator[](int i) util_assert(i < 2); return *(&x + i); } +# endif ccl_device_inline int2 make_int2(int x, int y) { int2 a = {x, y}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT2_IMPL_H__ */ diff --git a/intern/cycles/util/types_int3.h b/intern/cycles/util/types_int3.h index f88ff22ac35..e059ddd3660 100644 --- a/intern/cycles/util/types_int3.h +++ b/intern/cycles/util/types_int3.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT3_H__ -#define __UTIL_TYPES_INT3_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,10 +9,15 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct ccl_try_align(16) int3 { -# ifdef __KERNEL_SSE__ +# ifdef __KERNEL_GPU__ + /* Compact structure on the GPU. */ + int x, y, z; +# else + /* SIMD aligned structure for CPU. */ +# ifdef __KERNEL_SSE__ union { __m128i m128; struct { @@ -29,19 +33,21 @@ struct ccl_try_align(16) int3 __forceinline operator __m128i &(); __forceinline int3 &operator=(const int3 &a); -# else /* __KERNEL_SSE__ */ +# else /* __KERNEL_SSE__ */ int x, y, z, w; -# endif /* __KERNEL_SSE__ */ +# endif /* __KERNEL_SSE__ */ +# endif +# ifndef __KERNEL_GPU__ __forceinline int operator[](int i) const; __forceinline int &operator[](int i); +# endif }; -ccl_device_inline int3 make_int3(int i); ccl_device_inline int3 make_int3(int x, int y, int z); -ccl_device_inline void print_int3(const char *label, const int3 &a); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ -CCL_NAMESPACE_END +ccl_device_inline int3 make_int3(int i); +ccl_device_inline void print_int3(ccl_private const char *label, const int3 a); -#endif /* __UTIL_TYPES_INT3_H__ */ +CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_int3_impl.h b/intern/cycles/util/types_int3_impl.h index 1c49e97ad32..830dfa3c658 100644 --- a/intern/cycles/util/types_int3_impl.h +++ b/intern/cycles/util/types_int3_impl.h @@ -1,20 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT3_IMPL_H__ -#define __UTIL_TYPES_INT3_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ # ifdef __KERNEL_SSE__ __forceinline int3::int3() { @@ -45,6 +40,7 @@ __forceinline int3 &int3::operator=(const int3 &a) } # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline int int3::operator[](int i) const { util_assert(i >= 0); @@ -58,34 +54,37 @@ __forceinline int &int3::operator[](int i) util_assert(i < 3); return *(&x + i); } - -ccl_device_inline int3 make_int3(int i) -{ -# ifdef __KERNEL_SSE__ - int3 a(_mm_set1_epi32(i)); -# else - int3 a = {i, i, i, i}; # endif - return a; -} ccl_device_inline int3 make_int3(int x, int y, int z) { -# ifdef __KERNEL_SSE__ - int3 a(_mm_set_epi32(0, z, y, x)); +# if defined(__KERNEL_GPU__) + return {x, y, z}; +# elif defined(__KERNEL_SSE__) + return int3(_mm_set_epi32(0, z, y, x)); # else - int3 a = {x, y, z, 0}; + return {x, y, z, 0}; # endif +} - return a; +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ + +ccl_device_inline int3 make_int3(int i) +{ +#if defined(__KERNEL_GPU__) + return make_int3(i, i, i); +#elif defined(__KERNEL_SSE__) + return int3(_mm_set1_epi32(i)); +#else + return {i, i, i, i}; +#endif } -ccl_device_inline void print_int3(const char *label, const int3 &a) +ccl_device_inline void print_int3(ccl_private const char *label, const int3 a) { +#ifdef __KERNEL_PRINTF__ printf("%s: %d %d %d\n", label, a.x, a.y, a.z); +#endif } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT3_IMPL_H__ */ diff --git a/intern/cycles/util/types_int4.h b/intern/cycles/util/types_int4.h index 9d557c01344..1a13c03e60e 100644 --- a/intern/cycles/util/types_int4.h +++ b/intern/cycles/util/types_int4.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT4_H__ -#define __UTIL_TYPES_INT4_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,7 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct float3; struct float4; @@ -37,17 +36,18 @@ struct ccl_try_align(16) int4 int x, y, z, w; # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline int operator[](int i) const; __forceinline int &operator[](int i); +# endif }; -ccl_device_inline int4 make_int4(int i); ccl_device_inline int4 make_int4(int x, int y, int z, int w); -ccl_device_inline int4 make_int4(const float3 &f); -ccl_device_inline int4 make_int4(const float4 &f); -ccl_device_inline void print_int4(const char *label, const int4 &a); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ -CCL_NAMESPACE_END +ccl_device_inline int4 make_int4(int i); +ccl_device_inline int4 make_int4(const float3 f); +ccl_device_inline int4 make_int4(const float4 f); +ccl_device_inline void print_int4(ccl_private const char *label, const int4 a); -#endif /* __UTIL_TYPES_INT4_H__ */ +CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_int4_impl.h b/intern/cycles/util/types_int4_impl.h index 11e1ede6705..067794e67b4 100644 --- a/intern/cycles/util/types_int4_impl.h +++ b/intern/cycles/util/types_int4_impl.h @@ -1,20 +1,15 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_INT4_IMPL_H__ -#define __UTIL_TYPES_INT4_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." #endif -#ifndef __KERNEL_GPU__ -# include <cstdio> -#endif - CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ # ifdef __KERNEL_SSE__ __forceinline int4::int4() { @@ -45,6 +40,7 @@ __forceinline int4 &int4::operator=(const int4 &a) } # endif /* __KERNEL_SSE__ */ +# ifndef __KERNEL_GPU__ __forceinline int int4::operator[](int i) const { util_assert(i >= 0); @@ -58,55 +54,53 @@ __forceinline int &int4::operator[](int i) util_assert(i < 4); return *(&x + i); } +# endif -ccl_device_inline int4 make_int4(int i) +ccl_device_inline int4 make_int4(int x, int y, int z, int w) { # ifdef __KERNEL_SSE__ - int4 a(_mm_set1_epi32(i)); + return int4(_mm_set_epi32(w, z, y, x)); # else - int4 a = {i, i, i, i}; + return {x, y, z, w}; # endif - return a; } -ccl_device_inline int4 make_int4(int x, int y, int z, int w) +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ + +ccl_device_inline int4 make_int4(int i) { -# ifdef __KERNEL_SSE__ - int4 a(_mm_set_epi32(w, z, y, x)); -# else - int4 a = {x, y, z, w}; -# endif - return a; +#ifdef __KERNEL_SSE__ + return int4(_mm_set1_epi32(i)); +#else + return make_int4(i, i, i, i); +#endif } -ccl_device_inline int4 make_int4(const float3 &f) +ccl_device_inline int4 make_int4(const float3 f) { -# ifdef __KERNEL_SSE__ - int4 a(_mm_cvtps_epi32(f.m128)); -# elif defined(__KERNEL_ONEAPI__) - int4 a = {(int)f.x, (int)f.y, (int)f.z, 0}; -# else - int4 a = {(int)f.x, (int)f.y, (int)f.z, (int)f.w}; -# endif - return a; +#if defined(__KERNEL_GPU__) + return make_int4((int)f.x, (int)f.y, (int)f.z, 0); +#elif defined(__KERNEL_SSE__) + return int4(_mm_cvtps_epi32(f.m128)); +#else + return make_int4((int)f.x, (int)f.y, (int)f.z, (int)f.w); +#endif } -ccl_device_inline int4 make_int4(const float4 &f) +ccl_device_inline int4 make_int4(const float4 f) { -# ifdef __KERNEL_SSE__ - int4 a(_mm_cvtps_epi32(f.m128)); -# else - int4 a = {(int)f.x, (int)f.y, (int)f.z, (int)f.w}; -# endif - return a; +#ifdef __KERNEL_SSE__ + return int4(_mm_cvtps_epi32(f.m128)); +#else + return make_int4((int)f.x, (int)f.y, (int)f.z, (int)f.w); +#endif } -ccl_device_inline void print_int4(const char *label, const int4 &a) +ccl_device_inline void print_int4(ccl_private const char *label, const int4 a) { +#ifdef __KERNEL_PRINTF__ printf("%s: %d %d %d %d\n", label, a.x, a.y, a.z, a.w); +#endif } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_INT4_IMPL_H__ */ diff --git a/intern/cycles/util/types_spectrum.h b/intern/cycles/util/types_spectrum.h new file mode 100644 index 00000000000..c59230b83ae --- /dev/null +++ b/intern/cycles/util/types_spectrum.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: Apache-2.0 + * Copyright 2022 Blender Foundation */ + +#ifndef __UTIL_TYPES_SPECTRUM_H__ +#define __UTIL_TYPES_SPECTRUM_H__ + +#ifndef __UTIL_TYPES_H__ +# error "Do not include this file directly, include util/types.h instead." +#endif + +CCL_NAMESPACE_BEGIN + +#define SPECTRUM_CHANNELS 3 +#define SPECTRUM_DATA_TYPE float3 +#define PACKED_SPECTRUM_DATA_TYPE packed_float3 + +using Spectrum = SPECTRUM_DATA_TYPE; +using PackedSpectrum = PACKED_SPECTRUM_DATA_TYPE; + +#define make_spectrum(f) CONCAT(make_, SPECTRUM_DATA_TYPE(f)) +#define load_spectrum(f) CONCAT(load_, SPECTRUM_DATA_TYPE(f)) +#define store_spectrum(s, f) CONCAT(store_, SPECTRUM_DATA_TYPE((s), (f))) + +#define zero_spectrum CONCAT(zero_, SPECTRUM_DATA_TYPE) +#define one_spectrum CONCAT(one_, SPECTRUM_DATA_TYPE) + +#define FOREACH_SPECTRUM_CHANNEL(counter) \ + for (int counter = 0; counter < SPECTRUM_CHANNELS; counter++) + +#define GET_SPECTRUM_CHANNEL(v, i) (((ccl_private float *)(&(v)))[i]) + +CCL_NAMESPACE_END + +#endif /* __UTIL_TYPES_SPECTRUM_H__ */ diff --git a/intern/cycles/util/types_uchar2.h b/intern/cycles/util/types_uchar2.h index 0b3c9bd0331..ce617248e6e 100644 --- a/intern/cycles/util/types_uchar2.h +++ b/intern/cycles/util/types_uchar2.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UCHAR2_H__ -#define __UTIL_TYPES_UCHAR2_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uchar2 { uchar x, y; +# ifndef __KERNEL_GPU__ __forceinline uchar operator[](int i) const; __forceinline uchar &operator[](int i); +# endif }; ccl_device_inline uchar2 make_uchar2(uchar x, uchar y); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UCHAR2_H__ */ diff --git a/intern/cycles/util/types_uchar2_impl.h b/intern/cycles/util/types_uchar2_impl.h index a7254d5eaf2..9f3f3a4efb9 100644 --- a/intern/cycles/util/types_uchar2_impl.h +++ b/intern/cycles/util/types_uchar2_impl.h @@ -10,7 +10,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ uchar uchar2::operator[](int i) const { util_assert(i >= 0); @@ -24,13 +25,14 @@ uchar &uchar2::operator[](int i) util_assert(i < 2); return *(&x + i); } +# endif ccl_device_inline uchar2 make_uchar2(uchar x, uchar y) { uchar2 a = {x, y}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_uchar3.h b/intern/cycles/util/types_uchar3.h index fc213502ada..aed04c4775e 100644 --- a/intern/cycles/util/types_uchar3.h +++ b/intern/cycles/util/types_uchar3.h @@ -10,16 +10,18 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uchar3 { uchar x, y, z; +# ifndef __KERNEL_GPU__ __forceinline uchar operator[](int i) const; __forceinline uchar &operator[](int i); +# endif }; ccl_device_inline uchar3 make_uchar3(uchar x, uchar y, uchar z); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_uchar3_impl.h b/intern/cycles/util/types_uchar3_impl.h index 0c24ffb488a..83eb3c99b3c 100644 --- a/intern/cycles/util/types_uchar3_impl.h +++ b/intern/cycles/util/types_uchar3_impl.h @@ -10,7 +10,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ uchar uchar3::operator[](int i) const { util_assert(i >= 0); @@ -24,13 +25,14 @@ uchar &uchar3::operator[](int i) util_assert(i < 3); return *(&x + i); } +# endif ccl_device_inline uchar3 make_uchar3(uchar x, uchar y, uchar z) { uchar3 a = {x, y, z}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_uchar4.h b/intern/cycles/util/types_uchar4.h index a2a2c945aaa..fb13a98875e 100644 --- a/intern/cycles/util/types_uchar4.h +++ b/intern/cycles/util/types_uchar4.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UCHAR4_H__ -#define __UTIL_TYPES_UCHAR4_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uchar4 { uchar x, y, z, w; +# ifndef __KERNEL_GPU__ __forceinline uchar operator[](int i) const; __forceinline uchar &operator[](int i); +# endif }; ccl_device_inline uchar4 make_uchar4(uchar x, uchar y, uchar z, uchar w); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UCHAR4_H__ */ diff --git a/intern/cycles/util/types_uchar4_impl.h b/intern/cycles/util/types_uchar4_impl.h index 8ec6213a37d..244bb98f883 100644 --- a/intern/cycles/util/types_uchar4_impl.h +++ b/intern/cycles/util/types_uchar4_impl.h @@ -10,7 +10,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ uchar uchar4::operator[](int i) const { util_assert(i >= 0); @@ -24,13 +25,14 @@ uchar &uchar4::operator[](int i) util_assert(i < 4); return *(&x + i); } +# endif ccl_device_inline uchar4 make_uchar4(uchar x, uchar y, uchar z, uchar w) { uchar4 a = {x, y, z, w}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/types_uint2.h b/intern/cycles/util/types_uint2.h index faa0955f903..4d76b628088 100644 --- a/intern/cycles/util/types_uint2.h +++ b/intern/cycles/util/types_uint2.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT2_H__ -#define __UTIL_TYPES_UINT2_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uint2 { uint x, y; +# ifndef __KERNEL_GPU__ __forceinline uint operator[](uint i) const; __forceinline uint &operator[](uint i); +# endif }; ccl_device_inline uint2 make_uint2(uint x, uint y); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT2_H__ */ diff --git a/intern/cycles/util/types_uint2_impl.h b/intern/cycles/util/types_uint2_impl.h index cac0ba6b531..b508aaf2543 100644 --- a/intern/cycles/util/types_uint2_impl.h +++ b/intern/cycles/util/types_uint2_impl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT2_IMPL_H__ -#define __UTIL_TYPES_UINT2_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ __forceinline uint uint2::operator[](uint i) const { util_assert(i < 2); @@ -22,14 +22,13 @@ __forceinline uint &uint2::operator[](uint i) util_assert(i < 2); return *(&x + i); } +# endif ccl_device_inline uint2 make_uint2(uint x, uint y) { uint2 a = {x, y}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT2_IMPL_H__ */ diff --git a/intern/cycles/util/types_uint3.h b/intern/cycles/util/types_uint3.h index 3ff87bfc791..b1571716fc7 100644 --- a/intern/cycles/util/types_uint3.h +++ b/intern/cycles/util/types_uint3.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT3_H__ -#define __UTIL_TYPES_UINT3_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uint3 { uint x, y, z; +# ifndef __KERNEL_GPU__ __forceinline uint operator[](uint i) const; __forceinline uint &operator[](uint i); +# endif }; ccl_device_inline uint3 make_uint3(uint x, uint y, uint z); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT3_H__ */ diff --git a/intern/cycles/util/types_uint3_impl.h b/intern/cycles/util/types_uint3_impl.h index 221883a1adb..d36c9f52de9 100644 --- a/intern/cycles/util/types_uint3_impl.h +++ b/intern/cycles/util/types_uint3_impl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT3_IMPL_H__ -#define __UTIL_TYPES_UINT3_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ __forceinline uint uint3::operator[](uint i) const { util_assert(i < 3); @@ -22,14 +22,13 @@ __forceinline uint &uint3::operator[](uint i) util_assert(i < 3); return *(&x + i); } +# endif ccl_device_inline uint3 make_uint3(uint x, uint y, uint z) { uint3 a = {x, y, z}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT3_IMPL_H__ */ diff --git a/intern/cycles/util/types_uint4.h b/intern/cycles/util/types_uint4.h index 504095b2383..4982b30f577 100644 --- a/intern/cycles/util/types_uint4.h +++ b/intern/cycles/util/types_uint4.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT4_H__ -#define __UTIL_TYPES_UINT4_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,17 +9,17 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct uint4 { uint x, y, z, w; +# ifndef __KERNEL_GPU__ __forceinline uint operator[](uint i) const; __forceinline uint &operator[](uint i); +# endif }; ccl_device_inline uint4 make_uint4(uint x, uint y, uint z, uint w); -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT4_H__ */ diff --git a/intern/cycles/util/types_uint4_impl.h b/intern/cycles/util/types_uint4_impl.h index d78db944a1f..1cfdb9e0992 100644 --- a/intern/cycles/util/types_uint4_impl.h +++ b/intern/cycles/util/types_uint4_impl.h @@ -1,8 +1,7 @@ /* SPDX-License-Identifier: Apache-2.0 * Copyright 2011-2022 Blender Foundation */ -#ifndef __UTIL_TYPES_UINT4_IMPL_H__ -#define __UTIL_TYPES_UINT4_IMPL_H__ +#pragma once #ifndef __UTIL_TYPES_H__ # error "Do not include this file directly, include util/types.h instead." @@ -10,7 +9,8 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ +# ifndef __KERNEL_GPU__ __forceinline uint uint4::operator[](uint i) const { util_assert(i < 3); @@ -22,14 +22,13 @@ __forceinline uint &uint4::operator[](uint i) util_assert(i < 3); return *(&x + i); } +# endif ccl_device_inline uint4 make_uint4(uint x, uint y, uint z, uint w) { uint4 a = {x, y, z, w}; return a; } -#endif /* !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) */ +#endif /* __KERNEL_NATIVE_VECTOR_TYPES__ */ CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_UINT4_IMPL_H__ */ diff --git a/intern/cycles/util/types_ushort4.h b/intern/cycles/util/types_ushort4.h index 9a6e12095ba..aef36f63285 100644 --- a/intern/cycles/util/types_ushort4.h +++ b/intern/cycles/util/types_ushort4.h @@ -10,7 +10,7 @@ CCL_NAMESPACE_BEGIN -#if !defined(__KERNEL_GPU__) || defined(__KERNEL_ONEAPI__) +#ifndef __KERNEL_NATIVE_VECTOR_TYPES__ struct ushort4 { uint16_t x, y, z, w; diff --git a/intern/cycles/util/types_vector3.h b/intern/cycles/util/types_vector3.h deleted file mode 100644 index 2e0d68e1bd0..00000000000 --- a/intern/cycles/util/types_vector3.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -#ifndef __UTIL_TYPES_VECTOR3_H__ -#define __UTIL_TYPES_VECTOR3_H__ - -#ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util/types.h instead." -#endif - -CCL_NAMESPACE_BEGIN - -#ifndef __KERNEL_GPU__ -template<typename T> class vector3 { - public: - T x, y, z; - - __forceinline vector3(); - __forceinline vector3(const T &a); - __forceinline vector3(const T &x, const T &y, const T &z); -}; -#endif /* __KERNEL_GPU__ */ - -CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_VECTOR3_H__ */ diff --git a/intern/cycles/util/types_vector3_impl.h b/intern/cycles/util/types_vector3_impl.h deleted file mode 100644 index a765780e2d3..00000000000 --- a/intern/cycles/util/types_vector3_impl.h +++ /dev/null @@ -1,30 +0,0 @@ -/* SPDX-License-Identifier: Apache-2.0 - * Copyright 2011-2022 Blender Foundation */ - -#ifndef __UTIL_TYPES_VECTOR3_IMPL_H__ -#define __UTIL_TYPES_VECTOR3_IMPL_H__ - -#ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util/types.h instead." -#endif - -CCL_NAMESPACE_BEGIN - -#ifndef __KERNEL_GPU__ -template<typename T> ccl_always_inline vector3<T>::vector3() -{ -} - -template<typename T> ccl_always_inline vector3<T>::vector3(const T &a) : x(a), y(a), z(a) -{ -} - -template<typename T> -ccl_always_inline vector3<T>::vector3(const T &x, const T &y, const T &z) : x(x), y(y), z(z) -{ -} -#endif /* __KERNEL_GPU__ */ - -CCL_NAMESPACE_END - -#endif /* __UTIL_TYPES_VECTOR3_IMPL_H__ */ diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index c681dc368bb..0ac3a234946 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -5,6 +5,7 @@ set(INC . ../clog ../glew-mx + ../../source/blender/blenlib ../../source/blender/imbuf ../../source/blender/makesdna ) @@ -25,6 +26,7 @@ set(SRC intern/GHOST_ISystemPaths.cpp intern/GHOST_ModifierKeys.cpp intern/GHOST_Path-api.cpp + intern/GHOST_PathUtils.cpp intern/GHOST_Rect.cpp intern/GHOST_System.cpp intern/GHOST_TimerManager.cpp @@ -59,6 +61,7 @@ set(SRC intern/GHOST_EventTrackpad.h intern/GHOST_EventWheel.h intern/GHOST_ModifierKeys.h + intern/GHOST_PathUtils.h intern/GHOST_System.h intern/GHOST_SystemPaths.h intern/GHOST_TimerManager.h diff --git a/intern/ghost/intern/GHOST_Context.cpp b/intern/ghost/intern/GHOST_Context.cpp index f9aa80dc13d..f4b3d08ffc5 100644 --- a/intern/ghost/intern/GHOST_Context.cpp +++ b/intern/ghost/intern/GHOST_Context.cpp @@ -35,7 +35,7 @@ bool win32_silent_chk(bool result) bool win32_chk(bool result, const char *file, int line, const char *text) { if (!result) { - LPTSTR formattedMsg = NULL; + LPTSTR formattedMsg = nullptr; DWORD error = GetLastError(); @@ -87,12 +87,12 @@ bool win32_chk(bool result, const char *file, int line, const char *text) default: { count = FormatMessage((FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS), - NULL, + nullptr, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)(&formattedMsg), 0, - NULL); + nullptr); msg = count > 0 ? formattedMsg : "<no system message>\n"; break; @@ -113,8 +113,9 @@ bool win32_chk(bool result, const char *file, int line, const char *text) SetLastError(NO_ERROR); - if (count != 0) + if (count != 0) { LocalFree(formattedMsg); + } } return result; diff --git a/intern/ghost/intern/GHOST_DropTargetX11.cpp b/intern/ghost/intern/GHOST_DropTargetX11.cpp index 252a8bfd095..4da3c7c996d 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.cpp +++ b/intern/ghost/intern/GHOST_DropTargetX11.cpp @@ -7,6 +7,7 @@ #include "GHOST_DropTargetX11.h" #include "GHOST_Debug.h" +#include "GHOST_PathUtils.h" #include "GHOST_utildefines.h" #include <cassert> @@ -97,89 +98,10 @@ GHOST_DropTargetX11::~GHOST_DropTargetX11() } } -/* Based on: https://stackoverflow.com/a/2766963/432509 */ - -using DecodeState_e = enum DecodeState_e { - /** Searching for an ampersand to convert. */ - STATE_SEARCH = 0, - /** Convert the two proceeding characters from hex. */ - STATE_CONVERTING -}; - -void GHOST_DropTargetX11::UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn) -{ - unsigned int i; - unsigned int len = strlen(encodedIn); - DecodeState_e state = STATE_SEARCH; - int j; - unsigned int asciiCharacter; - char tempNumBuf[3] = {0}; - bool bothDigits = true; - - memset(decodedOut, 0, bufferSize); - - for (i = 0; i < len; ++i) { - switch (state) { - case STATE_SEARCH: - if (encodedIn[i] != '%') { - strncat(decodedOut, &encodedIn[i], 1); - assert((int)strlen(decodedOut) < bufferSize); - break; - } - - /* We are now converting */ - state = STATE_CONVERTING; - break; - - case STATE_CONVERTING: - bothDigits = true; - - /* Create a buffer to hold the hex. For example, if %20, this - * buffer would hold 20 (in ASCII) */ - memset(tempNumBuf, 0, sizeof(tempNumBuf)); - - /* Conversion complete (i.e. don't convert again next iter) */ - state = STATE_SEARCH; - - strncpy(tempNumBuf, &encodedIn[i], 2); - - /* Ensure both characters are hexadecimal */ - - for (j = 0; j < 2; ++j) { - if (!isxdigit(tempNumBuf[j])) { - bothDigits = false; - } - } - - if (!bothDigits) { - break; - } - /* Convert two hexadecimal characters into one character */ - sscanf(tempNumBuf, "%x", &asciiCharacter); - - /* Ensure we aren't going to overflow */ - assert((int)strlen(decodedOut) < bufferSize); - - /* Concatenate this character onto the output */ - strncat(decodedOut, (char *)&asciiCharacter, 1); - - /* Skip the next character */ - i++; - break; - } - } -} - char *GHOST_DropTargetX11::FileUrlDecode(char *fileUrl) { if (strncmp(fileUrl, "file://", 7) == 0) { - /* assume one character of encoded URL can be expanded to 4 chars max */ - int decodedSize = 4 * strlen(fileUrl) + 1; - char *decodedPath = (char *)malloc(decodedSize); - - UrlDecode(decodedPath, decodedSize, fileUrl + 7); - - return decodedPath; + return GHOST_URL_decode_alloc(fileUrl + 7); } return nullptr; diff --git a/intern/ghost/intern/GHOST_DropTargetX11.h b/intern/ghost/intern/GHOST_DropTargetX11.h index f0ef27697e1..db73ddff70f 100644 --- a/intern/ghost/intern/GHOST_DropTargetX11.h +++ b/intern/ghost/intern/GHOST_DropTargetX11.h @@ -65,14 +65,6 @@ class GHOST_DropTargetX11 { void *getURIListGhostData(unsigned char *dropBuffer, int dropBufferSize); /** - * Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`) - * \param decodedOut: - buffer for decoded URL. - * \param bufferSize: - size of output buffer. - * \param encodedIn: - input encoded buffer to be decoded. - */ - void UrlDecode(char *decodedOut, int bufferSize, const char *encodedIn); - - /** * Fully decode file URL (i.e. converts `file:///a%20b/test` to `/a b/test`) * \param fileUrl: - file path URL to be fully decoded. * \return decoded file path (result should be free-d). diff --git a/intern/ghost/intern/GHOST_Event.h b/intern/ghost/intern/GHOST_Event.h index 0813378a819..1e1c428e2ae 100644 --- a/intern/ghost/intern/GHOST_Event.h +++ b/intern/ghost/intern/GHOST_Event.h @@ -22,7 +22,7 @@ class GHOST_Event : public GHOST_IEvent { * \param window: The generating window (or NULL if system event). */ GHOST_Event(uint64_t msec, GHOST_TEventType type, GHOST_IWindow *window) - : m_type(type), m_time(msec), m_window(window), m_data(NULL) + : m_type(type), m_time(msec), m_window(window), m_data(nullptr) { } diff --git a/intern/ghost/intern/GHOST_NDOFManager.cpp b/intern/ghost/intern/GHOST_NDOFManager.cpp index d58fb90f63e..746e3532b03 100644 --- a/intern/ghost/intern/GHOST_NDOFManager.cpp +++ b/intern/ghost/intern/GHOST_NDOFManager.cpp @@ -411,8 +411,9 @@ static bool nearHomePosition(GHOST_TEventNDOFMotionData *ndof, float threshold) bool GHOST_NDOFManager::sendMotionEvent() { - if (!m_motionEventPending) + if (!m_motionEventPending) { return false; + } m_motionEventPending = false; /* Any pending motion is handled right now. */ diff --git a/intern/ghost/intern/GHOST_Path-api.cpp b/intern/ghost/intern/GHOST_Path-api.cpp index 1b1c72d8a4b..58f36dc096d 100644 --- a/intern/ghost/intern/GHOST_Path-api.cpp +++ b/intern/ghost/intern/GHOST_Path-api.cpp @@ -49,10 +49,10 @@ const char *GHOST_getBinaryDir() return systemPaths ? systemPaths->getBinaryDir() : nullptr; } -void GHOST_addToSystemRecentFiles(const char *filename) +void GHOST_addToSystemRecentFiles(const char *filepath) { GHOST_ISystemPaths *systemPaths = GHOST_ISystemPaths::get(); if (systemPaths) { - systemPaths->addToSystemRecentFiles(filename); + systemPaths->addToSystemRecentFiles(filepath); } } diff --git a/intern/ghost/intern/GHOST_PathUtils.cpp b/intern/ghost/intern/GHOST_PathUtils.cpp new file mode 100644 index 00000000000..3b57480039a --- /dev/null +++ b/intern/ghost/intern/GHOST_PathUtils.cpp @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2010 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup GHOST + */ + +#include <cassert> +#include <cctype> +#include <cstdio> +#include <cstdlib> +#include <cstring> + +#include "GHOST_PathUtils.h" +#include "GHOST_Types.h" + +/* Based on: https://stackoverflow.com/a/2766963/432509 */ + +using DecodeState_e = enum DecodeState_e { + /** Searching for an ampersand to convert. */ + STATE_SEARCH = 0, + /** Convert the two proceeding characters from hex. */ + STATE_CONVERTING +}; + +void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src) +{ + const unsigned int buf_src_len = strlen(buf_src); + DecodeState_e state = STATE_SEARCH; + unsigned int ascii_character; + char temp_num_buf[3] = {0}; + + memset(buf_dst, 0, buf_dst_size); + + for (unsigned int i = 0; i < buf_src_len; i++) { + switch (state) { + case STATE_SEARCH: { + if (buf_src[i] != '%') { + strncat(buf_dst, &buf_src[i], 1); + assert((int)strlen(buf_dst) < buf_dst_size); + break; + } + + /* We are now converting. */ + state = STATE_CONVERTING; + break; + } + case STATE_CONVERTING: { + bool both_digits = true; + + /* Create a buffer to hold the hex. For example, if `%20`, + * this buffer would hold 20 (in ASCII). */ + memset(temp_num_buf, 0, sizeof(temp_num_buf)); + + /* Conversion complete (i.e. don't convert again next iteration). */ + state = STATE_SEARCH; + + strncpy(temp_num_buf, &buf_src[i], 2); + + /* Ensure both characters are hexadecimal. */ + for (int j = 0; j < 2; j++) { + if (!isxdigit(temp_num_buf[j])) { + both_digits = false; + } + } + + if (!both_digits) { + break; + } + /* Convert two hexadecimal characters into one character. */ + sscanf(temp_num_buf, "%x", &ascii_character); + + /* Ensure we aren't going to overflow. */ + assert((int)strlen(buf_dst) < buf_dst_size); + + /* Concatenate this character onto the output. */ + strncat(buf_dst, (char *)&ascii_character, 1); + + /* Skip the next character. */ + i++; + break; + } + } + } +} + +char *GHOST_URL_decode_alloc(const char *buf_src) +{ + /* Assume one character of encoded URL can be expanded to 4 chars max. */ + const size_t decoded_size_max = 4 * strlen(buf_src) + 1; + char *buf_dst = (char *)malloc(decoded_size_max); + GHOST_URL_decode(buf_dst, decoded_size_max, buf_src); + const size_t decoded_size = strlen(buf_dst) + 1; + if (decoded_size != decoded_size_max) { + char *buf_dst_trim = (char *)malloc(decoded_size); + memcpy(buf_dst_trim, buf_dst, decoded_size); + free(buf_dst); + buf_dst = buf_dst_trim; + } + return buf_dst; +} diff --git a/intern/ghost/intern/GHOST_PathUtils.h b/intern/ghost/intern/GHOST_PathUtils.h new file mode 100644 index 00000000000..26a31d1f5c6 --- /dev/null +++ b/intern/ghost/intern/GHOST_PathUtils.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2010 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup GHOST + */ + +#pragma once + +/** + * Decode URL (i.e. converts `file:///a%20b/test` to `file:///a b/test`) + * + * \param buf_dst: Buffer for decoded URL. + * \param buf_dst_maxlen: Size of output buffer. + * \param buf_src: Input encoded buffer to be decoded. + */ +void GHOST_URL_decode(char *buf_dst, int buf_dst_size, const char *buf_src); +/** + * A version of #GHOST_URL_decode that allocates the string & returns it. + * + * \param buf_src: Input encoded buffer to be decoded. + * \return The decoded output buffer. + */ +char *GHOST_URL_decode_alloc(const char *buf_src); diff --git a/intern/ghost/intern/GHOST_SystemWayland.cpp b/intern/ghost/intern/GHOST_SystemWayland.cpp index ebb52bf08cb..18d91d43057 100644 --- a/intern/ghost/intern/GHOST_SystemWayland.cpp +++ b/intern/ghost/intern/GHOST_SystemWayland.cpp @@ -11,7 +11,9 @@ #include "GHOST_EventDragnDrop.h" #include "GHOST_EventKey.h" #include "GHOST_EventWheel.h" +#include "GHOST_PathUtils.h" #include "GHOST_TimerManager.h" +#include "GHOST_WaylandUtils.h" #include "GHOST_WindowManager.h" #include "GHOST_utildefines.h" @@ -82,6 +84,10 @@ static void output_handle_done(void *data, struct wl_output *wl_output); static bool use_gnome_confine_hack = false; #endif +#define XKB_STATE_MODS_ALL \ + (enum xkb_state_component)(XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | \ + XKB_STATE_MODS_LOCKED | XKB_STATE_MODS_EFFECTIVE) + /* -------------------------------------------------------------------- */ /** \name Inline Event Codes * @@ -306,6 +312,20 @@ struct input_t { */ struct xkb_state *xkb_state_empty_with_numlock = nullptr; + /** + * Cache result of `xkb_keymap_mod_get_index` + * so every time a modifier is accessed a string lookup isn't required. + * Be sure to check for #XKB_MOD_INVALID before using. + */ + struct { + xkb_mod_index_t shift; /* #XKB_MOD_NAME_SHIFT */ + xkb_mod_index_t caps; /* #XKB_MOD_NAME_CAPS */ + xkb_mod_index_t ctrl; /* #XKB_MOD_NAME_CTRL */ + xkb_mod_index_t alt; /* #XKB_MOD_NAME_ALT */ + xkb_mod_index_t num; /* #XKB_MOD_NAME_NUM */ + xkb_mod_index_t logo; /* #XKB_MOD_NAME_LOGO */ + } xkb_keymap_mod_index; + struct { /** Key repetition in character per second. */ int32_t rate = 0; @@ -1138,6 +1158,7 @@ static void data_device_handle_enter(void *data, input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_offer_dnd_mutex}; + delete input->data_offer_dnd; input->data_offer_dnd = static_cast<data_offer_t *>(wl_data_offer_get_user_data(id)); data_offer_t *data_offer = input->data_offer_dnd; @@ -1197,8 +1218,6 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat input_t *input = static_cast<input_t *>(data); std::lock_guard lock{input->data_offer_dnd_mutex}; - CLOG_INFO(LOG, 2, "drop"); - data_offer_t *data_offer = input->data_offer_dnd; const std::string mime_receive = *std::find_first_of(mime_preference_order.begin(), @@ -1206,6 +1225,8 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat data_offer->types.begin(), data_offer->types.end()); + CLOG_INFO(LOG, 2, "drop mime_recieve=%s", mime_receive.c_str()); + auto read_uris_fn = [](input_t *const input, data_offer_t *data_offer, wl_surface *surface, @@ -1214,9 +1235,15 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat const std::string data = read_pipe(data_offer, mime_receive, nullptr); + CLOG_INFO( + LOG, 2, "drop_read_uris mime_receive=%s, data=%s", mime_receive.c_str(), data.c_str()); + wl_data_offer_finish(data_offer->id); wl_data_offer_destroy(data_offer->id); + if (input->data_offer_dnd == data_offer) { + input->data_offer_dnd = nullptr; + } delete data_offer; data_offer = nullptr; @@ -1224,7 +1251,9 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat if (mime_receive == mime_text_uri) { static constexpr const char *file_proto = "file://"; - static constexpr const char *crlf = "\r\n"; + /* NOTE: some applications CRLF (`\r\n`) GTK3 for e.g. & others don't `pcmanfm-qt`. + * So support both, once `\n` is found, strip the preceding `\r` if found. */ + static constexpr const char *lf = "\n"; GHOST_WindowWayland *win = ghost_wl_surface_user_data(surface); std::vector<std::string> uris; @@ -1233,13 +1262,18 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat while (true) { pos = data.find(file_proto, pos); const size_t start = pos + sizeof(file_proto) - 1; - pos = data.find(crlf, pos); - const size_t end = pos; + pos = data.find(lf, pos); if (pos == std::string::npos) { break; } + /* Account for 'CRLF' case. */ + size_t end = pos; + if (data[end - 1] == '\r') { + end -= 1; + } uris.push_back(data.substr(start, end - start)); + CLOG_INFO(LOG, 2, "drop_read_uris pos=%zu, text_uri=\"%s\"", start, uris.back().c_str()); } GHOST_TStringArray *flist = static_cast<GHOST_TStringArray *>( @@ -1247,10 +1281,10 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat flist->count = int(uris.size()); flist->strings = static_cast<uint8_t **>(malloc(uris.size() * sizeof(uint8_t *))); for (size_t i = 0; i < uris.size(); i++) { - flist->strings[i] = static_cast<uint8_t *>(malloc((uris[i].size() + 1) * sizeof(uint8_t))); - memcpy(flist->strings[i], uris[i].data(), uris[i].size() + 1); + flist->strings[i] = (uint8_t *)GHOST_URL_decode_alloc(uris[i].c_str()); } + CLOG_INFO(LOG, 2, "drop_read_uris_fn file_count=%d", flist->count); const wl_fixed_t scale = win->scale(); system->pushEvent(new GHOST_EventDragnDrop(system->getMilliSeconds(), GHOST_kEventDraggingDropDone, @@ -1263,12 +1297,13 @@ static void data_device_handle_drop(void *data, struct wl_data_device * /*wl_dat else if (ELEM(mime_receive, mime_text_plain, mime_text_utf8)) { /* TODO: enable use of internal functions 'txt_insert_buf' and * 'text_update_edited' to behave like dropped text was pasted. */ + CLOG_INFO(LOG, 2, "drop_read_uris_fn (text_plain, text_utf8), unhandled!"); } wl_display_roundtrip(system->display()); }; /* Pass in `input->focus_dnd` instead of accessing it from `input` since the leave callback - * (#data_device_leave) will clear the value once this function starts. */ + * (#data_device_handle_leave) will clear the value once this function starts. */ std::thread read_thread(read_uris_fn, input, data_offer, input->focus_dnd, mime_receive); read_thread.detach(); } @@ -2086,6 +2121,13 @@ static void keyboard_handle_keymap(void *data, } } + input->xkb_keymap_mod_index.shift = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_SHIFT); + input->xkb_keymap_mod_index.caps = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CAPS); + input->xkb_keymap_mod_index.ctrl = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_CTRL); + input->xkb_keymap_mod_index.alt = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_ALT); + input->xkb_keymap_mod_index.num = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_NUM); + input->xkb_keymap_mod_index.logo = xkb_keymap_mod_get_index(keymap, XKB_MOD_NAME_LOGO); + xkb_keymap_unref(keymap); } @@ -2099,7 +2141,7 @@ static void keyboard_handle_enter(void *data, struct wl_keyboard * /*wl_keyboard*/, const uint32_t serial, struct wl_surface *surface, - struct wl_array * /*keys*/) + struct wl_array *keys) { if (!ghost_wl_surface_own(surface)) { CLOG_INFO(LOG, 2, "enter (skipped)"); @@ -2110,6 +2152,49 @@ static void keyboard_handle_enter(void *data, input_t *input = static_cast<input_t *>(data); input->keyboard.serial = serial; input->keyboard.wl_surface = surface; + + if (keys->size != 0) { + /* If there are any keys held when activating the window, + * modifiers will be compared against the input state, + * only enabling modifiers that were previously disabled. */ + + const xkb_mod_mask_t state = xkb_state_serialize_mods(input->xkb_state, XKB_STATE_MODS_ALL); + uint32_t *key; + WL_ARRAY_FOR_EACH (key, keys) { + const xkb_keycode_t key_code = *key + EVDEV_OFFSET; + const xkb_keysym_t sym = xkb_state_key_get_one_sym(input->xkb_state, key_code); + GHOST_TKey gkey = GHOST_kKeyUnknown; + +#define MOD_TEST(state, mod) ((mod != XKB_MOD_INVALID) && (state & (1 << mod))) +#define MOD_TEST_CASE(xkb_key, ghost_key, mod_index) \ + case xkb_key: \ + if (!MOD_TEST(state, input->xkb_keymap_mod_index.mod_index)) { \ + gkey = ghost_key; \ + } \ + break + + switch (sym) { + MOD_TEST_CASE(XKB_KEY_Shift_L, GHOST_kKeyLeftShift, shift); + MOD_TEST_CASE(XKB_KEY_Shift_R, GHOST_kKeyRightShift, shift); + MOD_TEST_CASE(XKB_KEY_Control_L, GHOST_kKeyLeftControl, ctrl); + MOD_TEST_CASE(XKB_KEY_Control_R, GHOST_kKeyRightControl, ctrl); + MOD_TEST_CASE(XKB_KEY_Alt_L, GHOST_kKeyLeftAlt, alt); + MOD_TEST_CASE(XKB_KEY_Alt_R, GHOST_kKeyRightAlt, alt); + MOD_TEST_CASE(XKB_KEY_Super_L, GHOST_kKeyOS, logo); + MOD_TEST_CASE(XKB_KEY_Super_R, GHOST_kKeyOS, logo); + } + +#undef MOD_TEST +#undef MOD_TEST_CASE + + if (gkey != GHOST_kKeyUnknown) { + GHOST_IWindow *win = ghost_wl_surface_user_data(surface); + GHOST_SystemWayland *system = input->system; + input->system->pushEvent( + new GHOST_EventKey(system->getMilliSeconds(), GHOST_kEventKeyDown, win, gkey, false)); + } + } + } } /** @@ -2336,7 +2421,13 @@ static void keyboard_handle_modifiers(void *data, const uint32_t mods_locked, const uint32_t group) { - CLOG_INFO(LOG, 2, "modifiers"); + CLOG_INFO(LOG, + 2, + "modifiers (depressed=%u, latched=%u, locked=%u, group=%u)", + mods_depressed, + mods_latched, + mods_locked, + group); input_t *input = static_cast<input_t *>(data); xkb_state_update_mask(input->xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); @@ -2473,7 +2564,7 @@ static void xdg_output_handle_logical_size(void *data, * done (we can't match exactly because fractional scaling can't be * detected otherwise), then override if necessary. */ if ((output->size_logical[0] == width) && (output->scale_fractional == wl_fixed_from_int(1))) { - GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale"); + GHOST_PRINT("xdg_output scale did not match, overriding with wl_output scale\n"); #ifdef USE_GNOME_CONFINE_HACK /* Use a bug in GNOME to check GNOME is in use. If the bug is fixed this won't cause an issue @@ -2923,32 +3014,36 @@ GHOST_TSuccess GHOST_SystemWayland::getModifierKeys(GHOST_ModifierKeys &keys) co return GHOST_kFailure; } - static const xkb_state_component mods_all = xkb_state_component( - XKB_STATE_MODS_DEPRESSED | XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED | - XKB_STATE_MODS_EFFECTIVE); + input_t *input = d->inputs[0]; bool val; - /* NOTE: XKB doesn't seem to differentiate between left/right modifiers. */ + /* NOTE: XKB doesn't differentiate between left/right modifiers + * for it's internal modifier state storage. */ + const xkb_mod_mask_t state = xkb_state_serialize_mods(input->xkb_state, XKB_STATE_MODS_ALL); + +#define MOD_TEST(state, mod) ((mod != XKB_MOD_INVALID) && (state & (1 << mod))) - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_SHIFT, mods_all) == 1; + val = MOD_TEST(state, input->xkb_keymap_mod_index.shift); keys.set(GHOST_kModifierKeyLeftShift, val); keys.set(GHOST_kModifierKeyRightShift, val); - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_ALT, mods_all) == 1; + val = MOD_TEST(state, input->xkb_keymap_mod_index.alt); keys.set(GHOST_kModifierKeyLeftAlt, val); keys.set(GHOST_kModifierKeyRightAlt, val); - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_CTRL, mods_all) == 1; + val = MOD_TEST(state, input->xkb_keymap_mod_index.ctrl); keys.set(GHOST_kModifierKeyLeftControl, val); keys.set(GHOST_kModifierKeyRightControl, val); - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_LOGO, mods_all) == 1; + val = MOD_TEST(state, input->xkb_keymap_mod_index.logo); keys.set(GHOST_kModifierKeyOS, val); - val = xkb_state_mod_name_is_active(d->inputs[0]->xkb_state, XKB_MOD_NAME_NUM, mods_all) == 1; + val = MOD_TEST(state, input->xkb_keymap_mod_index.num); keys.set(GHOST_kModifierKeyNum, val); +#undef MOD_TEST + return GHOST_kSuccess; } diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 00cc5f3af8f..0494e462bfc 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -287,7 +287,7 @@ uint8_t GHOST_SystemX11::getNumDisplays() const void GHOST_SystemX11::getMainDisplayDimensions(uint32_t &width, uint32_t &height) const { if (m_display) { - /* NOTE(campbell): for this to work as documented, + /* NOTE(@campbellbarton): for this to work as documented, * we would need to use Xinerama check r54370 for code that did this, * we've since removed since its not worth the extra dependency. */ getAllDisplayDimensions(width, height); @@ -514,8 +514,9 @@ static void destroyIMCallback(XIM /*xim*/, XPointer ptr, XPointer /*data*/) bool GHOST_SystemX11::openX11_IM() { - if (!m_display) + if (!m_display) { return false; + } /* set locale modifiers such as `@im=ibus` specified by XMODIFIERS. */ XSetLocaleModifiers(""); @@ -585,7 +586,7 @@ struct init_timestamp_data { Time timestamp; }; -static Bool init_timestamp_scanner(Display *, XEvent *event, XPointer arg) +static Bool init_timestamp_scanner(Display * /*display*/, XEvent *event, XPointer arg) { init_timestamp_data *data = reinterpret_cast<init_timestamp_data *>(arg); switch (event->type) { @@ -673,11 +674,12 @@ bool GHOST_SystemX11::processEvents(bool waitForEvent) GHOST_WindowX11 *window = findGhostWindow(xevent.xany.window); if (window && !window->getX11_XIC() && window->createX11_XIC()) { GHOST_PRINT("XIM input context created\n"); - if (xevent.type == KeyPress) + if (xevent.type == KeyPress) { /* we can assume the window has input focus * here, because key events are received only * when the window is focused. */ XSetICFocus(window->getX11_XIC()); + } } } } @@ -1321,10 +1323,12 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #if defined(WITH_X11_XINPUT) && defined(X_HAVE_UTF8_STRING) XIC xic = window->getX11_XIC(); if (xic) { - if (xe->type == FocusIn) + if (xe->type == FocusIn) { XSetICFocus(xic); - else + } + else { XUnsetICFocus(xic); + } } #endif @@ -2381,10 +2385,11 @@ class DialogData { /* Is the mouse inside the given button */ bool isInsideButton(XEvent &e, uint button_num) { - return ((e.xmotion.y > height - padding_y - button_height) && - (e.xmotion.y < height - padding_y) && - (e.xmotion.x > width - (padding_x + button_width) * button_num) && - (e.xmotion.x < width - padding_x - (padding_x + button_width) * (button_num - 1))); + return ( + (e.xmotion.y > (int)(height - padding_y - button_height)) && + (e.xmotion.y < (int)(height - padding_y)) && + (e.xmotion.x > (int)(width - (padding_x + button_width) * button_num)) && + (e.xmotion.x < (int)(width - padding_x - (padding_x + button_width) * (button_num - 1)))); } }; diff --git a/intern/ghost/intern/GHOST_TimerManager.cpp b/intern/ghost/intern/GHOST_TimerManager.cpp index 504cdbfb6c8..e54c2515029 100644 --- a/intern/ghost/intern/GHOST_TimerManager.cpp +++ b/intern/ghost/intern/GHOST_TimerManager.cpp @@ -71,10 +71,11 @@ uint64_t GHOST_TimerManager::nextFireTime() TTimerVector::iterator iter; for (iter = m_timers.begin(); iter != m_timers.end(); ++iter) { - uint64_t next = (*iter)->getNext(); + const uint64_t next = (*iter)->getNext(); - if (next < smallest) + if (next < smallest) { smallest = next; + } } return smallest; @@ -86,8 +87,9 @@ bool GHOST_TimerManager::fireTimers(uint64_t time) bool anyProcessed = false; for (iter = m_timers.begin(); iter != m_timers.end(); ++iter) { - if (fireTimer(time, *iter)) + if (fireTimer(time, *iter)) { anyProcessed = true; + } } return anyProcessed; @@ -113,9 +115,7 @@ bool GHOST_TimerManager::fireTimer(uint64_t time, GHOST_TimerTask *task) return true; } - else { - return false; - } + return false; } void GHOST_TimerManager::disposeTimers() diff --git a/intern/ghost/intern/GHOST_TimerManager.h b/intern/ghost/intern/GHOST_TimerManager.h index 090a84d1f14..4458a107190 100644 --- a/intern/ghost/intern/GHOST_TimerManager.h +++ b/intern/ghost/intern/GHOST_TimerManager.h @@ -87,7 +87,7 @@ class GHOST_TimerManager { */ void disposeTimers(); - typedef std::vector<GHOST_TimerTask *> TTimerVector; + using TTimerVector = std::vector<GHOST_TimerTask *>; /** The list with event consumers. */ TTimerVector m_timers; diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index 2276e90387b..83c608435b0 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -147,16 +147,19 @@ static XVisualInfo *x11_visualinfo_from_glx(Display *display, /* take a frame buffer config that has alpha cap */ for (int i = 0; i < nbfbconfig; i++) { XVisualInfo *visual = (XVisualInfo *)glXGetVisualFromFBConfig(display, fbconfigs[i]); - if (!visual) + if (!visual) { continue; + } /* if we don't need a alpha background, the first config will do, otherwise * test the alphaMask as it won't necessarily be present */ if (needAlpha) { XRenderPictFormat *pict_format = XRenderFindVisualFormat(display, visual->visual); - if (!pict_format) + if (!pict_format) { continue; - if (pict_format->direct.alphaMask <= 0) + } + if (pict_format->direct.alphaMask <= 0) { continue; + } } *fbconfig = fbconfigs[i]; @@ -487,8 +490,9 @@ static Bool destroyICCallback(XIC /*xic*/, XPointer ptr, XPointer /*data*/) bool GHOST_WindowX11::createX11_XIC() { XIM xim = m_system->getX11_XIM(); - if (!xim) + if (!xim) { return false; + } XICCallback destroy; destroy.callback = (XICProc)destroyICCallback; @@ -536,17 +540,21 @@ void GHOST_WindowX11::refreshXInputDevices() XEventClass ev; DeviceMotionNotify(xtablet.Device, xtablet.MotionEvent, ev); - if (ev) + if (ev) { xevents.push_back(ev); + } DeviceButtonPress(xtablet.Device, xtablet.PressEvent, ev); - if (ev) + if (ev) { xevents.push_back(ev); + } ProximityIn(xtablet.Device, xtablet.ProxInEvent, ev); - if (ev) + if (ev) { xevents.push_back(ev); + } ProximityOut(xtablet.Device, xtablet.ProxOutEvent, ev); - if (ev) + if (ev) { xevents.push_back(ev); + } } XSelectExtensionEvent(m_display, m_window, xevents.data(), (int)xevents.size()); diff --git a/intern/ghost/test/gears/GHOST_Test.cpp b/intern/ghost/test/gears/GHOST_Test.cpp index 5a43543d163..f5ef2527d75 100644 --- a/intern/ghost/test/gears/GHOST_Test.cpp +++ b/intern/ghost/test/gears/GHOST_Test.cpp @@ -559,10 +559,12 @@ bool Application::processEvent(GHOST_IEvent *event) break; case GHOST_kKeyS: // toggle mono and stereo - if (stereo) + if (stereo) { stereo = false; - else + } + else { stereo = true; + } break; case GHOST_kKeyT: @@ -680,8 +682,9 @@ int main(int /*argc*/, char ** /*argv*/) if (lresult == ERROR_SUCCESS) printf("Successfully set value for key\n"); regkey.Close(); - if (lresult == ERROR_SUCCESS) + if (lresult == ERROR_SUCCESS) { printf("Successfully closed key\n"); + } // regkey.Write("2"); } #endif // WIN32 diff --git a/intern/ghost/test/multitest/MultiTest.c b/intern/ghost/test/multitest/MultiTest.c index 157e4f1b0f2..99b88dfb525 100644 --- a/intern/ghost/test/multitest/MultiTest.c +++ b/intern/ghost/test/multitest/MultiTest.c @@ -179,37 +179,44 @@ static void mainwindow_do_key(MainWindow *mw, GHOST_TKey key, int press) { switch (key) { case GHOST_kKeyC: - if (press) + if (press) { GHOST_SetCursorShape(mw->win, (GHOST_TStandardCursor)(rand() % (GHOST_kStandardCursorNumCursors))); + } break; case GHOST_kKeyLeftBracket: - if (press) + if (press) { GHOST_SetCursorVisibility(mw->win, 0); + } break; case GHOST_kKeyRightBracket: - if (press) + if (press) { GHOST_SetCursorVisibility(mw->win, 1); + } break; case GHOST_kKeyE: - if (press) + if (press) { multitestapp_toggle_extra_window(mw->app); + } break; case GHOST_kKeyQ: - if (press) + if (press) { multitestapp_exit(mw->app); + } break; case GHOST_kKeyT: - if (press) + if (press) { mainwindow_log(mw, "TextTest~|`hello`\"world\",<>/"); + } break; case GHOST_kKeyR: if (press) { int i; mainwindow_log(mw, "Invalidating window 10 times"); - for (i = 0; i < 10; i++) + for (i = 0; i < 10; i++) { GHOST_InvalidateWindow(mw->win); + } } break; case GHOST_kKeyF11: @@ -328,9 +335,7 @@ MainWindow *mainwindow_new(MultiTestApp *app) return mw; } - else { - return NULL; - } + return NULL; } void mainwindow_free(MainWindow *mw) diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index fca19fb9731..64987548a11 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -199,6 +199,15 @@ extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT; #ifndef NDEBUG extern const char *(*MEM_name_ptr)(void *vmemh); +/** + * Change the debugging name/string assigned to the memory allocated at \a vmemh. Only affects the + * guarded allocator. The name must be a static string, because only a pointer to it is stored! + * + * Handy when debugging leaking memory allocated by some often called, generic function with a + * unspecific name. A caller with more info can set a more specific name, and see which call to the + * generic function allocates the leaking memory. + */ +extern void (*MEM_name_ptr_set)(void *vmemh, const char *str) ATTR_NONNULL(); #endif /** diff --git a/intern/guardedalloc/intern/mallocn.c b/intern/guardedalloc/intern/mallocn.c index f7979168799..63f06ced31d 100644 --- a/intern/guardedalloc/intern/mallocn.c +++ b/intern/guardedalloc/intern/mallocn.c @@ -49,6 +49,7 @@ size_t (*MEM_get_peak_memory)(void) = MEM_lockfree_get_peak_memory; #ifndef NDEBUG const char *(*MEM_name_ptr)(void *vmemh) = MEM_lockfree_name_ptr; +void (*MEM_name_ptr_set)(void *vmemh, const char *str) = MEM_lockfree_name_ptr_set; #endif void *aligned_malloc(size_t size, size_t alignment) @@ -128,6 +129,7 @@ void MEM_use_lockfree_allocator(void) #ifndef NDEBUG MEM_name_ptr = MEM_lockfree_name_ptr; + MEM_name_ptr_set = MEM_lockfree_name_ptr_set; #endif } @@ -159,5 +161,6 @@ void MEM_use_guarded_allocator(void) #ifndef NDEBUG MEM_name_ptr = MEM_guarded_name_ptr; + MEM_name_ptr_set = MEM_guarded_name_ptr_set; #endif } diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c index 8bf1680e6f8..cd4b99ecde8 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.c +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c @@ -1199,4 +1199,18 @@ const char *MEM_guarded_name_ptr(void *vmemh) return "MEM_guarded_name_ptr(NULL)"; } + +void MEM_guarded_name_ptr_set(void *vmemh, const char *str) +{ + if (!vmemh) { + return; + } + + MemHead *memh = vmemh; + memh--; + memh->name = str; + if (memh->prev) { + MEMNEXT(memh->prev)->nextname = str; + } +} #endif /* NDEBUG */ diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h index f8b16ff6ddf..ce5683a04ae 100644 --- a/intern/guardedalloc/intern/mallocn_intern.h +++ b/intern/guardedalloc/intern/mallocn_intern.h @@ -131,6 +131,7 @@ void MEM_lockfree_reset_peak_memory(void); size_t MEM_lockfree_get_peak_memory(void) ATTR_WARN_UNUSED_RESULT; #ifndef NDEBUG const char *MEM_lockfree_name_ptr(void *vmemh); +void MEM_lockfree_name_ptr_set(void *vmemh, const char *str); #endif /* Prototypes for fully guarded allocator functions */ @@ -174,6 +175,7 @@ void MEM_guarded_reset_peak_memory(void); size_t MEM_guarded_get_peak_memory(void) ATTR_WARN_UNUSED_RESULT; #ifndef NDEBUG const char *MEM_guarded_name_ptr(void *vmemh); +void MEM_guarded_name_ptr_set(void *vmemh, const char *str); #endif #ifdef __cplusplus diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c index 300e2000a14..b5ee539ff4d 100644 --- a/intern/guardedalloc/intern/mallocn_lockfree_impl.c +++ b/intern/guardedalloc/intern/mallocn_lockfree_impl.c @@ -426,4 +426,8 @@ const char *MEM_lockfree_name_ptr(void *vmemh) return "MEM_lockfree_name_ptr(NULL)"; } + +void MEM_lockfree_name_ptr_set(void *UNUSED(vmemh), const char *UNUSED(str)) +{ +} #endif /* NDEBUG */ diff --git a/intern/mantaflow/intern/MANTA_main.cpp b/intern/mantaflow/intern/MANTA_main.cpp index fc14c909f4d..f5f22dc700b 100644 --- a/intern/mantaflow/intern/MANTA_main.cpp +++ b/intern/mantaflow/intern/MANTA_main.cpp @@ -626,7 +626,7 @@ static void manta_python_main_module_restore(PyObject *main_mod) * access these variables, the same __main__ module has to be used every time. * * Unfortunately, we also depend on the fact that mantaflow dumps variables into this module using - * PyRun_SimpleString. So we can't easily create a separate module without changing mantaflow. + * #PyRun_String. So we can't easily create a separate module without changing mantaflow. */ static PyObject *manta_main_module = nullptr; @@ -1161,7 +1161,7 @@ string MANTA::parseScript(const string &setup_string, FluidModifierData *fmd) return res.str(); } -/* Dirty hack: Needed to format paths from python code that is run via PyRun_SimpleString */ +/** Dirty hack: Needed to format paths from python code that is run via #PyRun_String. */ static string escapePath(string const &s) { string result = ""; diff --git a/release/datafiles/fonts/DejaVuSans.woff2 b/release/datafiles/fonts/DejaVuSans.woff2 Binary files differnew file mode 100644 index 00000000000..a391596a421 --- /dev/null +++ b/release/datafiles/fonts/DejaVuSans.woff2 diff --git a/release/datafiles/fonts/DejaVuSansMono.woff2 b/release/datafiles/fonts/DejaVuSansMono.woff2 Binary files differnew file mode 100644 index 00000000000..cf200e12fff --- /dev/null +++ b/release/datafiles/fonts/DejaVuSansMono.woff2 diff --git a/release/datafiles/fonts/Noto Sans CJK Regular.woff2 b/release/datafiles/fonts/Noto Sans CJK Regular.woff2 Binary files differnew file mode 100644 index 00000000000..5d3854b6bf7 --- /dev/null +++ b/release/datafiles/fonts/Noto Sans CJK Regular.woff2 diff --git a/release/datafiles/fonts/NotoEmoji-VariableFont_wght.woff2 b/release/datafiles/fonts/NotoEmoji-VariableFont_wght.woff2 Binary files differnew file mode 100644 index 00000000000..4d019787bca --- /dev/null +++ b/release/datafiles/fonts/NotoEmoji-VariableFont_wght.woff2 diff --git a/release/datafiles/fonts/NotoSansArabic-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansArabic-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..8ee78b73e72 --- /dev/null +++ b/release/datafiles/fonts/NotoSansArabic-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansArmenian-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansArmenian-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..c6c1ed5c2cf --- /dev/null +++ b/release/datafiles/fonts/NotoSansArmenian-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansBengali-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansBengali-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..cdac12cc8e8 --- /dev/null +++ b/release/datafiles/fonts/NotoSansBengali-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansDevanagari-Regular.woff2 b/release/datafiles/fonts/NotoSansDevanagari-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..2cb157b2c51 --- /dev/null +++ b/release/datafiles/fonts/NotoSansDevanagari-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansEthiopic-Regular.woff2 b/release/datafiles/fonts/NotoSansEthiopic-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..dc272d98964 --- /dev/null +++ b/release/datafiles/fonts/NotoSansEthiopic-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansGeorgian-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansGeorgian-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..4ebc52f0b59 --- /dev/null +++ b/release/datafiles/fonts/NotoSansGeorgian-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansGujarati-Regular.woff2 b/release/datafiles/fonts/NotoSansGujarati-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..6e66a15b1cd --- /dev/null +++ b/release/datafiles/fonts/NotoSansGujarati-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..e752468775f --- /dev/null +++ b/release/datafiles/fonts/NotoSansGurmukhi-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..4f6033c916f --- /dev/null +++ b/release/datafiles/fonts/NotoSansHebrew-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansJavanese-Regular.woff2 b/release/datafiles/fonts/NotoSansJavanese-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..aeb0bbe8dab --- /dev/null +++ b/release/datafiles/fonts/NotoSansJavanese-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansKannada-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansKannada-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..56fbd8d8bce --- /dev/null +++ b/release/datafiles/fonts/NotoSansKannada-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansMalayalam-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansMalayalam-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..bdbce8a0b76 --- /dev/null +++ b/release/datafiles/fonts/NotoSansMalayalam-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansMath-Regular.woff2 b/release/datafiles/fonts/NotoSansMath-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..bb3baafeb7a --- /dev/null +++ b/release/datafiles/fonts/NotoSansMath-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansMyanmar-Regular.woff2 b/release/datafiles/fonts/NotoSansMyanmar-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..f18edac80ed --- /dev/null +++ b/release/datafiles/fonts/NotoSansMyanmar-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansSymbols-VariableFont_wght.woff2 b/release/datafiles/fonts/NotoSansSymbols-VariableFont_wght.woff2 Binary files differnew file mode 100644 index 00000000000..98f940b813e --- /dev/null +++ b/release/datafiles/fonts/NotoSansSymbols-VariableFont_wght.woff2 diff --git a/release/datafiles/fonts/NotoSansSymbols2-Regular.woff2 b/release/datafiles/fonts/NotoSansSymbols2-Regular.woff2 Binary files differnew file mode 100644 index 00000000000..cefcc2d9c0d --- /dev/null +++ b/release/datafiles/fonts/NotoSansSymbols2-Regular.woff2 diff --git a/release/datafiles/fonts/NotoSansTamil-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansTamil-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..a3541942429 --- /dev/null +++ b/release/datafiles/fonts/NotoSansTamil-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansTelugu-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansTelugu-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..790235d3a71 --- /dev/null +++ b/release/datafiles/fonts/NotoSansTelugu-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/NotoSansThai-VariableFont_wdth,wght.woff2 b/release/datafiles/fonts/NotoSansThai-VariableFont_wdth,wght.woff2 Binary files differnew file mode 100644 index 00000000000..507255e6b5c --- /dev/null +++ b/release/datafiles/fonts/NotoSansThai-VariableFont_wdth,wght.woff2 diff --git a/release/datafiles/fonts/bmonofont-i18n.ttf b/release/datafiles/fonts/bmonofont-i18n.ttf Binary files differdeleted file mode 100644 index 08b3f723d61..00000000000 --- a/release/datafiles/fonts/bmonofont-i18n.ttf +++ /dev/null diff --git a/release/datafiles/fonts/droidsans.ttf b/release/datafiles/fonts/droidsans.ttf Binary files differdeleted file mode 100644 index b03e47f087e..00000000000 --- a/release/datafiles/fonts/droidsans.ttf +++ /dev/null diff --git a/release/datafiles/fonts/lastresort.woff2 b/release/datafiles/fonts/lastresort.woff2 Binary files differnew file mode 100644 index 00000000000..e5ad6f353f5 --- /dev/null +++ b/release/datafiles/fonts/lastresort.woff2 diff --git a/release/datafiles/splash.png b/release/datafiles/splash.png Binary files differindex d7c9cdd3a8f..eb1250cf5a5 100644 --- a/release/datafiles/splash.png +++ b/release/datafiles/splash.png diff --git a/release/scripts/modules/gpu_extras/presets.py b/release/scripts/modules/gpu_extras/presets.py index ac9fd3cc1ff..eba8d6c161d 100644 --- a/release/scripts/modules/gpu_extras/presets.py +++ b/release/scripts/modules/gpu_extras/presets.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later -def draw_circle_2d(position, color, radius, *, segments=32): +def draw_circle_2d(position, color, radius, *, segments=None): """ Draw a circle. @@ -11,10 +11,11 @@ def draw_circle_2d(position, color, radius, *, segments=32): :arg radius: Radius of the circle. :type radius: float :arg segments: How many segments will be used to draw the circle. - Higher values give besser results but the drawing will take longer. - :type segments: int + Higher values give better results but the drawing will take longer. + If None or not specified, an automatic value will be calculated. + :type segments: int or None """ - from math import sin, cos, pi + from math import sin, cos, pi, ceil, acos import gpu from gpu.types import ( GPUBatch, @@ -22,6 +23,12 @@ def draw_circle_2d(position, color, radius, *, segments=32): GPUVertFormat, ) + if segments is None: + max_pixel_error = 0.25 # TODO: multiply 0.5 by display dpi + segments = int(ceil(pi / acos(1.0 - max_pixel_error / radius))) + segments = max(segments, 8) + segments = min(segments, 1000) + if segments <= 0: raise ValueError("Amount of segments must be greater than 0.") diff --git a/release/scripts/modules/rna_keymap_ui.py b/release/scripts/modules/rna_keymap_ui.py index a7ad162ba66..73e9b6cd70f 100644 --- a/release/scripts/modules/rna_keymap_ui.py +++ b/release/scripts/modules/rna_keymap_ui.py @@ -11,8 +11,10 @@ __all__ = ( import bpy -from bpy.app.translations import pgettext_iface as iface_ -from bpy.app.translations import contexts as i18n_contexts +from bpy.app.translations import ( + contexts as i18n_contexts, + pgettext_iface as iface_, +) def _indented_layout(layout, level): diff --git a/release/scripts/startup/bl_operators/node.py b/release/scripts/startup/bl_operators/node.py index df4ca9ef170..ff9b5a06fb7 100644 --- a/release/scripts/startup/bl_operators/node.py +++ b/release/scripts/startup/bl_operators/node.py @@ -149,37 +149,6 @@ class NODE_OT_add_node(NodeAddOperator, Operator): bl_options = {'REGISTER', 'UNDO'} -# Add a node and link it to an existing socket -class NODE_OT_add_and_link_node(NodeAddOperator, Operator): - '''Add a node to the active tree and link to an existing socket''' - bl_idname = "node.add_and_link_node" - bl_label = "Add and Link Node" - bl_options = {'REGISTER', 'UNDO'} - - link_socket_index: IntProperty( - name="Link Socket Index", - description="Index of the socket to link", - ) - - def execute(self, context): - space = context.space_data - ntree = space.edit_tree - - node = self.create_node(context) - if not node: - return {'CANCELLED'} - - to_socket = getattr(context, "link_to_socket", None) - if to_socket: - ntree.links.new(node.outputs[self.link_socket_index], to_socket) - - from_socket = getattr(context, "link_from_socket", None) - if from_socket: - ntree.links.new(from_socket, node.inputs[self.link_socket_index]) - - return {'FINISHED'} - - class NODE_OT_add_search(NodeAddOperator, Operator): '''Add a node to the active tree''' bl_idname = "node.add_search" @@ -306,7 +275,6 @@ class NODE_OT_tree_path_parent(Operator): classes = ( NodeSetting, - NODE_OT_add_and_link_node, NODE_OT_add_node, NODE_OT_add_search, NODE_OT_collapse_hide_unused_toggle, diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 7e7dbbc387e..cbb5a63b754 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -3152,7 +3152,10 @@ class WM_OT_drop_blend_file(Operator): bl_label = "Handle dropped .blend file" bl_options = {'INTERNAL'} - filepath: StringProperty() + filepath: StringProperty( + subtype='FILE_PATH', + options={'SKIP_SAVE'}, + ) def invoke(self, context, _event): context.window_manager.popup_menu(self.draw_menu, title=bpy.path.basename(self.filepath), icon='QUESTION') diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 0f839eac126..963ffc60806 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -201,7 +201,7 @@ class DATA_PT_camera(CameraButtonsPanel, Panel): class DATA_PT_camera_dof(CameraButtonsPanel, Panel): bl_label = "Depth of Field" bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'} def draw_header(self, context): cam = context.camera @@ -228,7 +228,7 @@ class DATA_PT_camera_dof(CameraButtonsPanel, Panel): class DATA_PT_camera_dof_aperture(CameraButtonsPanel, Panel): bl_label = "Aperture" bl_parent_id = "DATA_PT_camera_dof" - COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_EEVEE_NEXT', 'BLENDER_WORKBENCH'} def draw(self, context): layout = self.layout diff --git a/release/scripts/startup/bl_ui/properties_data_light.py b/release/scripts/startup/bl_ui/properties_data_light.py index df3ad43e6de..2980592ee0b 100644 --- a/release/scripts/startup/bl_ui/properties_data_light.py +++ b/release/scripts/startup/bl_ui/properties_data_light.py @@ -18,7 +18,7 @@ class DataButtonsPanel: class DATA_PT_context_light(DataButtonsPanel, Panel): bl_label = "" bl_options = {'HIDE_HEADER'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} def draw(self, context): layout = self.layout @@ -36,7 +36,7 @@ class DATA_PT_context_light(DataButtonsPanel, Panel): class DATA_PT_preview(DataButtonsPanel, Panel): bl_label = "Preview" bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE'} def draw(self, context): self.layout.template_preview(context.light) @@ -62,7 +62,7 @@ class DATA_PT_light(DataButtonsPanel, Panel): class DATA_PT_EEVEE_light(DataButtonsPanel, Panel): bl_label = "Light" - COMPAT_ENGINES = {'BLENDER_EEVEE'} + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE'} def draw(self, context): layout = self.layout @@ -108,7 +108,7 @@ class DATA_PT_EEVEE_light_distance(DataButtonsPanel, Panel): bl_label = "Custom Distance" bl_parent_id = "DATA_PT_EEVEE_light" bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_EEVEE'} + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE'} @classmethod def poll(cls, context): @@ -256,7 +256,7 @@ class DATA_PT_area(DataButtonsPanel, Panel): class DATA_PT_spot(DataButtonsPanel, Panel): bl_label = "Spot Shape" bl_parent_id = "DATA_PT_EEVEE_light" - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} @classmethod def poll(cls, context): @@ -301,7 +301,7 @@ class DATA_PT_falloff_curve(DataButtonsPanel, Panel): class DATA_PT_custom_props_light(DataButtonsPanel, PropertyPanel, Panel): - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} + COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE_NEXT', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} _context_path = "object.data" _property_type = bpy.types.Light 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 44d82be8ab0..38522a1bf84 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -355,8 +355,7 @@ class GPENCIL_UL_annotation_layer(UIList): row = layout.row(align=True) - icon_xray = 'XRAY' if gpl.show_in_front else 'FACESEL' - row.prop(gpl, "show_in_front", text="", icon=icon_xray, emboss=False) + row.prop(gpl, "show_in_front", text="", icon='XRAY' if gpl.show_in_front else 'FACESEL', emboss=False) row.prop(gpl, "annotation_hide", text="", emboss=False) elif self.layout_type == 'GRID': diff --git a/release/scripts/startup/bl_ui/properties_particle.py b/release/scripts/startup/bl_ui/properties_particle.py index cd390eee970..0cef14d6a5d 100644 --- a/release/scripts/startup/bl_ui/properties_particle.py +++ b/release/scripts/startup/bl_ui/properties_particle.py @@ -2,8 +2,10 @@ import bpy from bpy.types import Panel, Menu from rna_prop_ui import PropertyPanel -from bpy.app.translations import pgettext_iface as iface_ -from bpy.app.translations import contexts as i18n_contexts +from bpy.app.translations import ( + contexts as i18n_contexts, + pgettext_iface as iface_, +) from bl_ui.utils import PresetPanel from bl_ui.properties_physics_common import ( diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py index e031b32247a..dafe32c5e5d 100644 --- a/release/scripts/startup/bl_ui/properties_render.py +++ b/release/scripts/startup/bl_ui/properties_render.py @@ -162,6 +162,64 @@ class RENDER_PT_eevee_motion_blur(RenderButtonsPanel, Panel): col.prop(props, "motion_blur_steps", text="Steps") +class RENDER_PT_eevee_next_motion_blur(RenderButtonsPanel, Panel): + bl_label = "Motion Blur" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'} + + @classmethod + def poll(cls, context): + return (context.engine in cls.COMPAT_ENGINES) + + def draw_header(self, context): + scene = context.scene + props = scene.eevee + self.layout.prop(props, "use_motion_blur", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + scene = context.scene + props = scene.eevee + + layout.active = props.use_motion_blur + col = layout.column() + col.prop(props, "motion_blur_position", text="Position") + col.prop(props, "motion_blur_shutter") + col.separator() + col.prop(props, "motion_blur_depth_scale") + col.prop(props, "motion_blur_steps", text="Steps") + + +class RENDER_PT_motion_blur_curve(RenderButtonsPanel, Panel): + bl_label = "Shutter Curve" + bl_parent_id = "RENDER_PT_eevee_next_motion_blur" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'} + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + scene = context.scene + rd = scene.render + layout.active = rd.use_motion_blur + + col = layout.column() + + col.template_curve_mapping(rd, "motion_blur_shutter_curve") + + col = layout.column(align=True) + row = col.row(align=True) + row.operator("render.shutter_curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' + row.operator("render.shutter_curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' + row.operator("render.shutter_curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' + row.operator("render.shutter_curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' + row.operator("render.shutter_curve_preset", icon='LINCURVE', text="").shape = 'LINE' + row.operator("render.shutter_curve_preset", icon='NOCURVE', text="").shape = 'MAX' + + class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel): bl_label = "Depth of Field" bl_options = {'DEFAULT_CLOSED'} @@ -190,6 +248,32 @@ class RENDER_PT_eevee_depth_of_field(RenderButtonsPanel, Panel): col.prop(props, "bokeh_overblur") +class RENDER_PT_eevee_next_depth_of_field(RenderButtonsPanel, Panel): + bl_label = "Depth of Field" + bl_options = {'DEFAULT_CLOSED'} + COMPAT_ENGINES = {'BLENDER_EEVEE_NEXT'} + + @classmethod + def poll(cls, context): + return (context.engine in cls.COMPAT_ENGINES) + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + scene = context.scene + props = scene.eevee + + col = layout.column() + col.prop(props, "bokeh_max_size") + col.prop(props, "bokeh_threshold") + col.prop(props, "bokeh_neighbor_max") + col.prop(props, "use_bokeh_jittered") + + col = layout.column() + col.active = props.use_bokeh_jittered + col.prop(props, "bokeh_overblur") + + class RENDER_PT_eevee_bloom(RenderButtonsPanel, Panel): bl_label = "Bloom" bl_options = {'DEFAULT_CLOSED'} @@ -739,12 +823,16 @@ class RENDER_PT_simplify_greasepencil(RenderButtonsPanel, Panel, GreasePencilSim classes = ( RENDER_PT_context, RENDER_PT_eevee_sampling, + RENDER_PT_eevee_next_sampling, RENDER_PT_eevee_ambient_occlusion, RENDER_PT_eevee_bloom, RENDER_PT_eevee_depth_of_field, + RENDER_PT_eevee_next_depth_of_field, RENDER_PT_eevee_subsurface_scattering, RENDER_PT_eevee_screen_space_reflections, RENDER_PT_eevee_motion_blur, + RENDER_PT_eevee_next_motion_blur, + RENDER_PT_motion_blur_curve, RENDER_PT_eevee_volumetric, RENDER_PT_eevee_volumetric_lighting, RENDER_PT_eevee_volumetric_shadows, @@ -754,10 +842,9 @@ classes = ( RENDER_PT_eevee_indirect_lighting, RENDER_PT_eevee_indirect_lighting_display, RENDER_PT_eevee_film, - - RENDER_PT_eevee_next_sampling, RENDER_PT_eevee_next_film, + RENDER_PT_gpencil, RENDER_PT_opengl_sampling, RENDER_PT_opengl_lighting, diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py index 01bd0adc8df..78aec096510 100644 --- a/release/scripts/startup/bl_ui/properties_view_layer.py +++ b/release/scripts/startup/bl_ui/properties_view_layer.py @@ -79,6 +79,7 @@ class VIEWLAYER_PT_eevee_next_layer_passes_data(ViewLayerButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False + scene = context.scene view_layer = context.view_layer col = layout.column() @@ -87,7 +88,9 @@ class VIEWLAYER_PT_eevee_next_layer_passes_data(ViewLayerButtonsPanel, Panel): col.prop(view_layer, "use_pass_mist") col.prop(view_layer, "use_pass_normal") col.prop(view_layer, "use_pass_position") - col.prop(view_layer, "use_pass_vector") + sub = col.column() + sub.active = not scene.eevee.use_motion_blur + sub.prop(view_layer, "use_pass_vector") class VIEWLAYER_PT_eevee_layer_passes_light(ViewLayerButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index dab06cc4993..118928ef9c6 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -1,8 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later import bpy from bpy.types import Header, Menu, Panel -from bpy.app.translations import pgettext_iface as iface_ -from bpy.app.translations import contexts as i18n_contexts +from bpy.app.translations import ( + pgettext_iface as iface_, + contexts as i18n_contexts, +) from bl_ui.utils import PresetPanel from bl_ui.properties_grease_pencil_common import ( AnnotationDataPanel, diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 38aa343e542..20021762d5a 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1881,7 +1881,7 @@ class _defs_image_uv_sculpt: if brush is None: return radius = brush.size - draw_circle_2d(xy, (1.0,) * 4, radius, segments=32) + draw_circle_2d(xy, (1.0,) * 4, radius) return generate_from_enum_ex( context, diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 52b2fb7f3da..8de9d5e0770 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -5,8 +5,10 @@ from bpy.types import ( Menu, Panel, ) -from bpy.app.translations import pgettext_iface as iface_ -from bpy.app.translations import contexts as i18n_contexts +from bpy.app.translations import ( + contexts as i18n_contexts, + pgettext_iface as iface_, +) # ----------------------------------------------------------------------------- @@ -2258,6 +2260,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): ({"property": "use_sculpt_tools_tilt"}, "T82877"), ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), + ({"property": "use_realtime_compositor"}, "T99210"), ), ) diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index 2f9050ba638..68fe9cafccc 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -6128,6 +6128,24 @@ class VIEW3D_PT_shading_render_pass(Panel): layout.prop(shading, "render_pass", text="") +class VIEW3D_PT_shading_compositor(Panel): + bl_space_type = 'VIEW_3D' + bl_region_type = 'HEADER' + bl_label = "Compositor" + bl_parent_id = 'VIEW3D_PT_shading' + + @classmethod + def poll(cls, context): + return (context.space_data.shading.type in {'MATERIAL', 'RENDERED'} and + context.preferences.experimental.use_realtime_compositor) + + def draw(self, context): + shading = context.space_data.shading + + layout = self.layout + layout.prop(shading, "use_compositor") + + class VIEW3D_PT_gizmo_display(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' @@ -7967,6 +7985,7 @@ classes = ( VIEW3D_PT_shading_options_shadow, VIEW3D_PT_shading_options_ssao, VIEW3D_PT_shading_render_pass, + VIEW3D_PT_shading_compositor, VIEW3D_PT_gizmo_display, VIEW3D_PT_overlay, VIEW3D_PT_overlay_guides, diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 8ba6e7318bb..28c15d9224c 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -151,14 +151,12 @@ add_subdirectory(io) add_subdirectory(functions) add_subdirectory(makesdna) add_subdirectory(makesrna) +add_subdirectory(compositor) if(WITH_BLENDER_THUMBNAILER) add_subdirectory(blendthumb) endif() -if(WITH_COMPOSITOR) - add_subdirectory(compositor) -endif() if(WITH_IMAGE_OPENEXR) add_subdirectory(imbuf/intern/openexr) diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc index 163197c8b67..fff1242f2ce 100644 --- a/source/blender/blendthumb/src/blendthumb_extract.cc +++ b/source/blender/blendthumb/src/blendthumb_extract.cc @@ -136,7 +136,7 @@ static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file, thumb->height = bytes_to_native_i32(&shape[4], endian_switch); /* Verify that image dimensions and data size make sense. */ - size_t data_size = block_size - 8; + size_t data_size = block_size - sizeof(shape); const uint64_t expected_size = static_cast<uint64_t>(thumb->width) * static_cast<uint64_t>(thumb->height) * 4; if (thumb->width < 0 || thumb->height < 0 || data_size != expected_size) { diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 83ca9158efc..75824ae056f 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -18,10 +18,10 @@ extern "C" { #define BLF_DATAFILES_FONTS_DIR "fonts" /* File name of the default variable-width font. */ -#define BLF_DEFAULT_PROPORTIONAL_FONT "droidsans.ttf" +#define BLF_DEFAULT_PROPORTIONAL_FONT "DejaVuSans.woff2" /* File name of the default fixed-pitch font. */ -#define BLF_DEFAULT_MONOSPACED_FONT "bmonofont-i18n.ttf" +#define BLF_DEFAULT_MONOSPACED_FONT "DejaVuSansMono.woff2" /* enable this only if needed (unused circa 2016) */ #define BLF_BLUR_ENABLE 0 @@ -351,6 +351,8 @@ enum { BLF_DEFAULT = 1 << 14, /** Must only be used as last font in the stack. */ BLF_LAST_RESORT = 1 << 15, + /** Failure to load this font. Don't try again. */ + BLF_BAD_FONT = 1 << 16, }; #define BLF_DRAW_STR_DUMMY_MAX 1024 diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index a1fcc17ca3f..36475321d4c 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -122,7 +122,7 @@ bool BLF_has_glyph(int fontid, unsigned int unicode) { FontBLF *font = blf_get(fontid); if (font) { - return FT_Get_Char_Index(font->face, unicode) != FT_Err_Ok; + return blf_get_char_index(font, unicode) != FT_Err_Ok; } return false; } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 038e73cc928..b3729b7a673 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -18,8 +18,9 @@ #include FT_FREETYPE_H #include FT_GLYPH_H -#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */ #include FT_MULTIPLE_MASTERS_H /* Variable font support. */ +#include FT_TRUETYPE_IDS_H /* Codepoint coverage constants. */ +#include FT_TRUETYPE_TABLES_H /* For TT_OS2 */ #include "MEM_guardedalloc.h" @@ -28,6 +29,7 @@ #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" +#include "BLI_path_util.h" #include "BLI_rect.h" #include "BLI_string.h" #include "BLI_string_utf8.h" @@ -52,9 +54,10 @@ BatchBLF g_batch; /* freetype2 handle ONLY for this file! */ -static FT_Library ft_lib; -static SpinLock ft_lib_mutex; -static SpinLock blf_glyph_cache_mutex; +static FT_Library ft_lib = NULL; + +/* Lock for FreeType library, used around face creation and deletion. */ +static ThreadMutex ft_lib_mutex; /* May be set to #UI_widgetbase_draw_cache_flush. */ static void (*blf_draw_cache_flush)(void) = NULL; @@ -63,19 +66,27 @@ static ft_pix blf_font_height_max_ft_pix(struct FontBLF *font); static ft_pix blf_font_width_max_ft_pix(struct FontBLF *font); /* -------------------------------------------------------------------- */ + +/* Return glyph id from charcode. */ +uint blf_get_char_index(struct FontBLF *font, uint charcode) +{ + return blf_ensure_face(font) ? FT_Get_Char_Index(font->face, charcode) : 0; +} + +/* -------------------------------------------------------------------- */ /** \name FreeType Utilities (Internal) * \{ */ -/* Convert a FreeType 26.6 value representing an unscaled design size to factional pixels. */ +/* Convert a FreeType 26.6 value representing an unscaled design size to fractional pixels. */ static ft_pix blf_unscaled_F26Dot6_to_pixels(FontBLF *font, FT_Pos value) { /* Scale value by font size using integer-optimized multiplication. */ - FT_Long scaled = FT_MulFix(value, font->face->size->metrics.x_scale); + FT_Long scaled = FT_MulFix(value, font->ft_size->metrics.x_scale); /* Copied from FreeType's FT_Get_Kerning (with FT_KERNING_DEFAULT), scaling down */ /* kerning distances at small ppem values so that they don't become too big. */ - if (font->face->size->metrics.x_ppem < 25) { - scaled = FT_MulDiv(scaled, font->face->size->metrics.x_ppem, 25); + if (font->ft_size->metrics.x_ppem < 25) { + scaled = FT_MulDiv(scaled, font->ft_size->metrics.x_ppem, 25); } return (ft_pix)scaled; @@ -294,7 +305,7 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph /* Small adjust if there is hinting. */ adjustment += g->lsb_delta - ((g_prev) ? g_prev->rsb_delta : 0); - if (FT_HAS_KERNING(font->face) && g_prev) { + if (FT_HAS_KERNING(font) && g_prev) { FT_Vector delta = {KERNING_ENTRY_UNSET}; /* Get unscaled kerning value from our cache if ASCII. */ @@ -303,7 +314,7 @@ BLI_INLINE ft_pix blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const Glyph } /* If not ASCII or not found in cache, ask FreeType for kerning. */ - if (UNLIKELY(delta.x == KERNING_ENTRY_UNSET)) { + if (UNLIKELY(font->face && delta.x == KERNING_ENTRY_UNSET)) { /* Note that this function sets delta values to zero on any error. */ FT_Get_Kerning(font->face, g_prev->idx, g->idx, FT_KERNING_UNSCALED, &delta); } @@ -836,7 +847,7 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, size_t i = 0, i_curr; rcti gbox_px; - if (str_len == 0) { + if (str_len == 0 || str[0] == 0) { /* early output. */ return; } @@ -923,8 +934,7 @@ static void blf_font_wrap_apply(FontBLF *font, int lines = 0; ft_pix pen_x_next = 0; - /* Space between lines needs to be aligned to the pixel grid (T97310). */ - ft_pix line_height = FT_PIX_FLOOR(blf_font_height_max_ft_pix(font)); + ft_pix line_height = blf_font_height_max_ft_pix(font); GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); @@ -1088,7 +1098,7 @@ int blf_font_count_missing_chars(FontBLF *font, } else { c = BLI_str_utf8_as_unicode_step(str, str_len, &i); - if (FT_Get_Char_Index((font)->face, c) == 0) { + if (blf_get_char_index(font, c) == 0) { missing++; } } @@ -1105,18 +1115,8 @@ int blf_font_count_missing_chars(FontBLF *font, static ft_pix blf_font_height_max_ft_pix(FontBLF *font) { - ft_pix height_max; - FT_Face face = font->face; - if (FT_IS_SCALABLE(face)) { - height_max = ft_pix_from_int((int)(face->ascender - face->descender) * - (int)face->size->metrics.y_ppem) / - (ft_pix)face->units_per_EM; - } - else { - height_max = (ft_pix)face->size->metrics.height; - } - /* can happen with size 1 fonts */ - return MAX2(height_max, ft_pix_from_int(1)); + /* Metrics.height is rounded to pixel. Force minimum of one pixel. */ + return MAX2((ft_pix)font->ft_size->metrics.height, ft_pix_from_int(1)); } int blf_font_height_max(FontBLF *font) @@ -1126,18 +1126,8 @@ int blf_font_height_max(FontBLF *font) static ft_pix blf_font_width_max_ft_pix(FontBLF *font) { - ft_pix width_max; - const FT_Face face = font->face; - if (FT_IS_SCALABLE(face)) { - width_max = ft_pix_from_int((int)(face->bbox.xMax - face->bbox.xMin) * - (int)face->size->metrics.x_ppem) / - (ft_pix)face->units_per_EM; - } - else { - width_max = (ft_pix)face->size->metrics.max_advance; - } - /* can happen with size 1 fonts */ - return MAX2(width_max, ft_pix_from_int(1)); + /* Metrics.max_advance is rounded to pixel. Force minimum of one pixel. */ + return MAX2((ft_pix)font->ft_size->metrics.max_advance, ft_pix_from_int(1)); } int blf_font_width_max(FontBLF *font) @@ -1147,17 +1137,17 @@ int blf_font_width_max(FontBLF *font) int blf_font_descender(FontBLF *font) { - return ft_pix_to_int((ft_pix)font->face->size->metrics.descender); + return ft_pix_to_int((ft_pix)font->ft_size->metrics.descender); } int blf_font_ascender(FontBLF *font) { - return ft_pix_to_int((ft_pix)font->face->size->metrics.ascender); + return ft_pix_to_int((ft_pix)font->ft_size->metrics.ascender); } char *blf_display_name(FontBLF *font) { - if (!font->face->family_name) { + if (!blf_ensure_face(font) || !font->face->family_name) { return NULL; } return BLI_sprintfN("%s %s", font->face->family_name, font->face->style_name); @@ -1172,16 +1162,17 @@ char *blf_display_name(FontBLF *font) int blf_font_init(void) { memset(&g_batch, 0, sizeof(g_batch)); - BLI_spin_init(&ft_lib_mutex); - BLI_spin_init(&blf_glyph_cache_mutex); - return FT_Init_FreeType(&ft_lib); + BLI_mutex_init(&ft_lib_mutex); + int err = FT_Init_FreeType(&ft_lib); + return err; } void blf_font_exit(void) { - FT_Done_FreeType(ft_lib); - BLI_spin_end(&ft_lib_mutex); - BLI_spin_end(&blf_glyph_cache_mutex); + BLI_mutex_end(&ft_lib_mutex); + if (ft_lib) { + FT_Done_FreeType(ft_lib); + } blf_batch_draw_exit(); } @@ -1240,18 +1231,32 @@ static void blf_font_fill(FontBLF *font) font->buf_info.col_init[3] = 0; font->ft_lib = ft_lib; - font->ft_lib_mutex = &ft_lib_mutex; - font->glyph_cache_mutex = &blf_glyph_cache_mutex; } -FontBLF *blf_font_new(const char *name, const char *filepath) +/** + * Create an FT_Face for this font if not already existing. + */ +bool blf_ensure_face(FontBLF *font) { - FontBLF *font; + if (font->face) { + return true; + } + + if (font->flags & BLF_BAD_FONT) { + return false; + } + FT_Error err; - char *mfile; - font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new"); - err = FT_New_Face(ft_lib, filepath, 0, &font->face); + BLI_mutex_lock(&ft_lib_mutex); + if (font->filepath) { + err = FT_New_Face(ft_lib, font->filepath, 0, &font->face); + } + if (font->mem) { + err = FT_New_Memory_Face(ft_lib, font->mem, (FT_Long)font->mem_size, 0, &font->face); + } + BLI_mutex_unlock(&ft_lib_mutex); + if (err) { if (ELEM(err, FT_Err_Unknown_File_Format, FT_Err_Unimplemented_Feature)) { printf("Format of this font file is not supported\n"); @@ -1259,8 +1264,8 @@ FontBLF *blf_font_new(const char *name, const char *filepath) else { printf("Error encountered while opening font file\n"); } - MEM_freeN(font); - return NULL; + font->flags |= BLF_BAD_FONT; + return false; } err = FT_Select_Charmap(font->face, FT_ENCODING_UNICODE); @@ -1272,28 +1277,31 @@ FontBLF *blf_font_new(const char *name, const char *filepath) } if (err) { printf("Can't set a character map!\n"); - FT_Done_Face(font->face); - MEM_freeN(font); - return NULL; + font->flags |= BLF_BAD_FONT; + return false; } - mfile = blf_dir_metrics_search(filepath); - if (mfile) { - err = FT_Attach_File(font->face, mfile); - if (err) { - fprintf(stderr, "FT_Attach_File failed to load '%s' with error %d\n", filepath, (int)err); + if (font->filepath) { + char *mfile = blf_dir_metrics_search(font->filepath); + if (mfile) { + err = FT_Attach_File(font->face, mfile); + if (err) { + fprintf(stderr, + "FT_Attach_File failed to load '%s' with error %d\n", + font->filepath, + (int)err); + } + MEM_freeN(mfile); } - MEM_freeN(mfile); } - if (FT_HAS_MULTIPLE_MASTERS(font->face)) { + font->ft_size = font->face->size; + font->face_flags = font->face->face_flags; + + if (FT_HAS_MULTIPLE_MASTERS(font)) { FT_Get_MM_Var(font->face, &(font->variations)); } - font->name = BLI_strdup(name); - font->filepath = BLI_strdup(filepath); - blf_font_fill(font); - /* Save TrueType table with bits to quickly test most unicode block coverage. */ TT_OS2 *os2_table = (TT_OS2 *)FT_Get_Sfnt_Table(font->face, FT_SFNT_OS2); if (os2_table) { @@ -1303,17 +1311,11 @@ FontBLF *blf_font_new(const char *name, const char *filepath) font->UnicodeRanges[3] = (uint)os2_table->ulUnicodeRange4; } - /* Detect "Last resort" fonts. They have everything. Usually except last 5 bits. */ - if (font->UnicodeRanges[0] == 0xffffffffU && font->UnicodeRanges[1] == 0xffffffffU && - font->UnicodeRanges[2] == 0xffffffffU && font->UnicodeRanges[3] >= 0x7FFFFFFU) { - font->flags |= BLF_LAST_RESORT; - } - - if (FT_IS_FIXED_WIDTH(font->face)) { + if (FT_IS_FIXED_WIDTH(font)) { font->flags |= BLF_MONOSPACED; } - if (FT_HAS_KERNING(font->face)) { + if (FT_HAS_KERNING(font) && !font->kerning_cache) { /* Create kerning cache table and fill with value indicating "unset". */ font->kerning_cache = MEM_mallocN(sizeof(KerningCacheBLF), __func__); for (uint i = 0; i < KERNING_CACHE_TABLE_SIZE; i++) { @@ -1323,49 +1325,118 @@ FontBLF *blf_font_new(const char *name, const char *filepath) } } - return font; + return true; } -void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, int mem_size) +typedef struct eFaceDetails { + char name[50]; + unsigned int coverage1; + unsigned int coverage2; + unsigned int coverage3; + unsigned int coverage4; +} eFaceDetails; + +/* Details about the fallback fonts we ship, so that we can load only when needed. */ +static const eFaceDetails static_face_details[] = { + {"lastresort.woff2", UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX}, + {"Noto Sans CJK Regular.woff2", 0x30000083L, 0x2BDF3C10L, 0x16L, 0}, + {"NotoEmoji-VariableFont_wght.woff2", 0x80000003L, 0x241E4ACL, 0x14000000L, 0x4000000L}, + {"NotoSansArabic-VariableFont_wdth,wght.woff2", + TT_UCR_ARABIC, + (uint)TT_UCR_ARABIC_PRESENTATION_FORMS_A, + TT_UCR_ARABIC_PRESENTATION_FORMS_B, + 0}, + {"NotoSansArmenian-VariableFont_wdth,wght.woff2", TT_UCR_ARMENIAN, 0, 0, 0}, + {"NotoSansBengali-VariableFont_wdth,wght.woff2", TT_UCR_BENGALI, 0, 0, 0}, + {"NotoSansDevanagari-Regular.woff2", TT_UCR_DEVANAGARI, 0, 0, 0}, + {"NotoSansEthiopic-Regular.woff2", 0, 0, TT_UCR_ETHIOPIC, 0}, + {"NotoSansGeorgian-VariableFont_wdth,wght.woff2", TT_UCR_GEORGIAN, 0, 0, 0}, + {"NotoSansGujarati-Regular.woff2", TT_UCR_GUJARATI, 0, 0, 0}, + {"NotoSansGurmukhi-VariableFont_wdth,wght.woff2", TT_UCR_GURMUKHI, 0, 0, 0}, + {"NotoSansHebrew-VariableFont_wdth,wght.woff2", TT_UCR_HEBREW, 0, 0, 0}, + {"NotoSansJavanese-Regular.woff2", 0x80000003L, 0x2000L, 0, 0}, + {"NotoSansKannada-VariableFont_wdth,wght.woff2", TT_UCR_KANNADA, 0, 0, 0}, + {"NotoSansMalayalam-VariableFont_wdth,wght.woff2", TT_UCR_MALAYALAM, 0, 0, 0}, + {"NotoSansMath-Regular.woff2", 0, TT_UCR_MATHEMATICAL_OPERATORS, 0, 0}, + {"NotoSansMyanmar-Regular.woff2", 0, 0, TT_UCR_MYANMAR, 0}, + {"NotoSansSymbols-VariableFont_wght.woff2", 0x3L, 0x200E4B4L, 0, 0}, + {"NotoSansSymbols2-Regular.woff2", 0x80000003L, 0x200E3E4L, 0x40020L, 0x580A048L}, + {"NotoSansTamil-VariableFont_wdth,wght.woff2", TT_UCR_TAMIL, 0, 0, 0}, + {"NotoSansTelugu-VariableFont_wdth,wght.woff2", TT_UCR_TELUGU, 0, 0, 0}, + {"NotoSansThai-VariableFont_wdth,wght.woff2", TT_UCR_THAI, 0, 0, 0}, +}; + +/* Create a new font from filename OR from passed memory pointer. */ +static FontBLF *blf_font_new_ex(const char *name, + const char *filepath, + const unsigned char *mem, + const size_t mem_size) { - FT_Open_Args open; + FontBLF *font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new"); - open.flags = FT_OPEN_MEMORY; - open.memory_base = (const FT_Byte *)mem; - open.memory_size = mem_size; - FT_Attach_Stream(font->face, &open); -} - -FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size) -{ - FontBLF *font; - FT_Error err; + font->name = BLI_strdup(name); + font->filepath = filepath ? BLI_strdup(filepath) : NULL; + if (mem) { + font->mem = (void *)mem; + font->mem_size = mem_size; + } + blf_font_fill(font); - font = (FontBLF *)MEM_callocN(sizeof(FontBLF), "blf_font_new_from_mem"); - err = FT_New_Memory_Face(ft_lib, mem, mem_size, 0, &font->face); - if (err) { - MEM_freeN(font); - return NULL; + BLI_mutex_init(&font->glyph_cache_mutex); + + /* If we have static details about this font we don't need to load the Face. */ + const eFaceDetails *static_details = NULL; + char filename[256]; + for (int i = 0; i < (int)ARRAY_SIZE(static_face_details); i++) { + BLI_split_file_part(font->filepath, filename, sizeof(filename)); + if (STREQ(static_face_details[i].name, filename)) { + static_details = &static_face_details[i]; + font->UnicodeRanges[0] = static_details->coverage1; + font->UnicodeRanges[1] = static_details->coverage2; + font->UnicodeRanges[2] = static_details->coverage3; + font->UnicodeRanges[3] = static_details->coverage4; + break; + } } - err = FT_Select_Charmap(font->face, ft_encoding_unicode); - if (err) { - printf("Can't set the unicode character map!\n"); - FT_Done_Face(font->face); - MEM_freeN(font); - return NULL; + if (!static_details) { + if (!blf_ensure_face(font)) { + blf_font_free(font); + return NULL; + } } - if (FT_HAS_MULTIPLE_MASTERS(font->face)) { - FT_Get_MM_Var(font->face, &(font->variations)); + /* Detect "Last resort" fonts. They have everything. Usually except last 5 bits. */ + if (font->UnicodeRanges[0] == 0xffffffffU && font->UnicodeRanges[1] == 0xffffffffU && + font->UnicodeRanges[2] == 0xffffffffU && font->UnicodeRanges[3] >= 0x7FFFFFFU) { + font->flags |= BLF_LAST_RESORT; } - font->name = BLI_strdup(name); - font->filepath = NULL; - blf_font_fill(font); return font; } +FontBLF *blf_font_new(const char *name, const char *filename) +{ + return blf_font_new_ex(name, filename, NULL, 0); +} + +FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, const size_t mem_size) +{ + return blf_font_new_ex(name, NULL, mem, mem_size); +} + +void blf_font_attach_from_mem(FontBLF *font, const unsigned char *mem, const size_t mem_size) +{ + FT_Open_Args open; + + open.flags = FT_OPEN_MEMORY; + open.memory_base = (const FT_Byte *)mem; + open.memory_size = (FT_Long)mem_size; + if (blf_ensure_face(font)) { + FT_Attach_Stream(font->face, &open); + } +} + void blf_font_free(FontBLF *font) { blf_glyph_cache_clear(font); @@ -1378,13 +1449,21 @@ void blf_font_free(FontBLF *font) FT_Done_MM_Var(ft_lib, font->variations); } - FT_Done_Face(font->face); + if (font->face) { + BLI_mutex_lock(&ft_lib_mutex); + FT_Done_Face(font->face); + BLI_mutex_unlock(&ft_lib_mutex); + font->face = NULL; + } if (font->filepath) { MEM_freeN(font->filepath); } if (font->name) { MEM_freeN(font->name); } + + BLI_mutex_end(&font->glyph_cache_mutex); + MEM_freeN(font); } @@ -1396,8 +1475,12 @@ void blf_font_free(FontBLF *font) bool blf_font_size(FontBLF *font, float size, unsigned int dpi) { + if (!blf_ensure_face(font)) { + return false; + } + /* FreeType uses fixed-point integers in 64ths. */ - FT_F26Dot6 ft_size = lroundf(size * 64.0f); + FT_UInt ft_size = round_fl_to_uint(size * 64.0f); /* Adjust our new size to be on even 64ths. */ size = (float)ft_size / 64.0f; diff --git a/source/blender/blenfont/intern/blf_font_default.c b/source/blender/blenfont/intern/blf_font_default.c index 1bde25b5776..63957fad003 100644 --- a/source/blender/blenfont/intern/blf_font_default.c +++ b/source/blender/blenfont/intern/blf_font_default.c @@ -66,8 +66,6 @@ void BLF_load_font_stack() } else { BLF_enable(font_id, BLF_DEFAULT); - /* TODO: FontBLF will later load FT_Face on demand. When this is in - * place we can drop this face now since we have all needed data. */ } } } diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 215f79e6795..f938174f92e 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -94,16 +94,16 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->bucket, 0, sizeof(gc->bucket)); /* Determine ideal fixed-width size for monospaced output. */ - FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); - if (gindex) { + FT_UInt gindex = blf_get_char_index(font, U'0'); + if (gindex && font->face) { FT_Fixed advance = 0; FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); /* Use CSS 'ch unit' width, advance of zero character. */ gc->fixed_width = (int)(advance >> 16); } else { - /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ - gc->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); + /* Font does not have a face or does not contain "0" so use CSS fallback of 1/2 of em. */ + gc->fixed_width = (int)((font->ft_size->metrics.height / 2) >> 6); } if (gc->fixed_width < 1) { gc->fixed_width = 1; @@ -115,7 +115,7 @@ static GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font) { - BLI_spin_lock(font->glyph_cache_mutex); + BLI_mutex_lock(&font->glyph_cache_mutex); GlyphCacheBLF *gc = blf_glyph_cache_find(font, font->size, font->dpi); @@ -128,7 +128,7 @@ GlyphCacheBLF *blf_glyph_cache_acquire(FontBLF *font) void blf_glyph_cache_release(FontBLF *font) { - BLI_spin_unlock(font->glyph_cache_mutex); + BLI_mutex_unlock(&font->glyph_cache_mutex); } static void blf_glyph_cache_free(GlyphCacheBLF *gc) @@ -152,13 +152,13 @@ void blf_glyph_cache_clear(FontBLF *font) { GlyphCacheBLF *gc; - BLI_spin_lock(font->glyph_cache_mutex); + BLI_mutex_lock(&font->glyph_cache_mutex); while ((gc = BLI_pophead(&font->cache))) { blf_glyph_cache_free(gc); } - BLI_spin_unlock(font->glyph_cache_mutex); + BLI_mutex_unlock(&font->glyph_cache_mutex); } /** @@ -565,7 +565,7 @@ static bool blf_font_has_coverage_bit(FontBLF *font, int coverage_bit) */ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode) { - FT_UInt glyph_index = FT_Get_Char_Index((*font)->face, charcode); + FT_UInt glyph_index = blf_get_char_index(*font, charcode); if (glyph_index) { return glyph_index; } @@ -584,7 +584,7 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode continue; } if (coverage_bit < 0 || blf_font_has_coverage_bit(f, coverage_bit)) { - glyph_index = FT_Get_Char_Index(f->face, charcode); + glyph_index = blf_get_char_index(f, charcode); if (glyph_index) { *font = f; return glyph_index; @@ -592,9 +592,13 @@ static FT_UInt blf_glyph_index_from_charcode(FontBLF **font, const uint charcode } } +#ifdef DEBUG + printf("Unicode character U+%04X not found in loaded fonts. \n", charcode); +#endif + /* Not found in the stack, return from Last Resort if there is one. */ if (last_resort) { - glyph_index = FT_Get_Char_Index(last_resort->face, charcode); + glyph_index = blf_get_char_index(last_resort, charcode); if (glyph_index) { *font = last_resort; return glyph_index; @@ -892,13 +896,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, int fixed_width) { if (glyph_font != settings_font) { - FT_Set_Char_Size(glyph_font->face, - 0, - ((FT_F26Dot6)(settings_font->size)) * 64, - settings_font->dpi, - settings_font->dpi); - glyph_font->size = settings_font->size; - glyph_font->dpi = settings_font->dpi; + blf_font_size(glyph_font, settings_font->size, settings_font->dpi); } /* We need to keep track if changes are still needed. */ @@ -955,7 +953,7 @@ static FT_GlyphSlot blf_glyph_render(FontBLF *settings_font, /* Fallback glyph transforms, but only if required and not yet done. */ if (weight != 0.0f && !weight_done) { - blf_glyph_transform_weight(glyph, weight, glyph->face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); + blf_glyph_transform_weight(glyph, weight, FT_IS_FIXED_WIDTH(glyph_font)); } if (slant != 0.0f && !slant_done) { blf_glyph_transform_slant(glyph, slant); @@ -984,11 +982,9 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) FontBLF *font_with_glyph = font; FT_UInt glyph_index = blf_glyph_index_from_charcode(&font_with_glyph, charcode); - /* Glyphs are dynamically created as needed by font rendering. this means that - * to make font rendering thread safe we have to do locking here. note that this - * must be a lock for the whole library and not just per font, because the font - * renderer uses a shared buffer internally. */ - BLI_spin_lock(font_with_glyph->ft_lib_mutex); + if (!blf_ensure_face(font_with_glyph)) { + return NULL; + } FT_GlyphSlot glyph = blf_glyph_render( font, font_with_glyph, glyph_index, charcode, gc->fixed_width); @@ -998,7 +994,6 @@ GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) g = blf_glyph_cache_add_glyph(font, gc, glyph, charcode, glyph_index); } - BLI_spin_unlock(font_with_glyph->ft_lib_mutex); return g; } diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 84037ff4bd0..221e656f096 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -14,7 +14,7 @@ struct ResultBLF; struct rctf; struct rcti; -/* Max number of fonts in memory. Take care that every font has a glyph cache per size/dpi, +/* Max number of FontBLFs in memory. Take care that every font has a glyph cache per size/dpi, * so we don't need load the same font with different size, just load one and call BLF_size. */ #define BLF_MAX_FONT 32 @@ -39,12 +39,16 @@ void blf_font_exit(void); bool blf_font_id_is_valid(int fontid); +uint blf_get_char_index(struct FontBLF *font, uint charcode); + +bool blf_ensure_face(struct FontBLF *font); + void blf_draw_buffer__start(struct FontBLF *font); void blf_draw_buffer__end(void); struct FontBLF *blf_font_new(const char *name, const char *filepath); -struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, int mem_size); -void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, int mem_size); +struct FontBLF *blf_font_new_from_mem(const char *name, const unsigned char *mem, size_t mem_size); +void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, size_t mem_size); /** * Change font's output size. Returns true if successful in changing the size. diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 5b55f4af0b8..dfe24c1aa47 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -240,9 +240,13 @@ typedef struct FontBLF { /* # of times this font was loaded */ unsigned int reference_count; - /** File-path or NULL. */ + /* Full path to font file or NULL if from memory. */ char *filepath; + /* Pointer to in-memory font, or NULL if from file. */ + void *mem; + size_t mem_size; + /* Copied from the SFNT OS/2 table. Bit flags for unicode blocks and ranges * considered "functional". Cached here because face might not always exist. * See: https://docs.microsoft.com/en-us/typography/opentype/spec/os2#ur */ @@ -319,17 +323,20 @@ typedef struct FontBLF { /* freetype2 lib handle. */ FT_Library ft_lib; - /* Mutex lock for library */ - SpinLock *ft_lib_mutex; - /* freetype2 face. */ FT_Face face; + /* Point to face->size or to cache's size. */ + FT_Size ft_size; + + /* Copy of the font->face->face_flags, in case we don't have a face loaded. */ + FT_Long face_flags; + /* data for buffer usage (drawing into a texture buffer) */ FontBufInfoBLF buf_info; /* Mutex lock for glyph cache. */ - SpinLock *glyph_cache_mutex; + ThreadMutex glyph_cache_mutex; } FontBLF; typedef struct DirBLF { diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 01c2ef988f2..770937688d7 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -188,10 +188,28 @@ template<typename T> class SimpleMixer { * \param default_value: Output value for an element that has not been affected by a #mix_in. */ SimpleMixer(MutableSpan<T> buffer, T default_value = {}) + : SimpleMixer(buffer, buffer.index_range(), default_value) + { + } + + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + SimpleMixer(MutableSpan<T> buffer, const IndexMask mask, T default_value = {}) : buffer_(buffer), default_value_(default_value), total_weights_(buffer.size(), 0.0f) { BLI_STATIC_ASSERT(std::is_trivial_v<T>, ""); - memset(buffer_.data(), 0, sizeof(T) * buffer_.size()); + mask.foreach_index([&](const int64_t i) { buffer_[i] = default_value_; }); + } + + /** + * Set a #value into the element with the given #index. + */ + void set(const int64_t index, const T &value, const float weight = 1.0f) + { + BLI_assert(weight >= 0.0f); + buffer_[index] = value * weight; + total_weights_[index] = weight; } /** @@ -209,7 +227,12 @@ template<typename T> class SimpleMixer { */ void finalize() { - for (const int64_t i : buffer_.index_range()) { + this->finalize(IndexMask(buffer_.size())); + } + + void finalize(const IndexMask mask) + { + mask.foreach_index([&](const int64_t i) { const float weight = total_weights_[i]; if (weight > 0.0f) { buffer_[i] *= 1.0f / weight; @@ -217,7 +240,7 @@ template<typename T> class SimpleMixer { else { buffer_[i] = default_value_; } - } + }); } }; @@ -237,9 +260,25 @@ class BooleanPropagationMixer { /** * \param buffer: Span where the interpolated values should be stored. */ - BooleanPropagationMixer(MutableSpan<bool> buffer) : buffer_(buffer) + BooleanPropagationMixer(MutableSpan<bool> buffer) + : BooleanPropagationMixer(buffer, buffer.index_range()) + { + } + + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + BooleanPropagationMixer(MutableSpan<bool> buffer, const IndexMask mask) : buffer_(buffer) + { + mask.foreach_index([&](const int64_t i) { buffer_[i] = false; }); + } + + /** + * Set a #value into the element with the given #index. + */ + void set(const int64_t index, const bool value, [[maybe_unused]] const float weight = 1.0f) { - buffer_.fill(false); + buffer_[index] = value; } /** @@ -256,6 +295,10 @@ class BooleanPropagationMixer { void finalize() { } + + void finalize(const IndexMask /*mask*/) + { + } }; /** @@ -277,8 +320,27 @@ class SimpleMixerWithAccumulationType { public: SimpleMixerWithAccumulationType(MutableSpan<T> buffer, T default_value = {}) + : SimpleMixerWithAccumulationType(buffer, buffer.index_range(), default_value) + { + } + + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + SimpleMixerWithAccumulationType(MutableSpan<T> buffer, + const IndexMask mask, + T default_value = {}) : buffer_(buffer), default_value_(default_value), accumulation_buffer_(buffer.size()) { + mask.foreach_index([&](const int64_t index) { buffer_[index] = default_value_; }); + } + + void set(const int64_t index, const T &value, const float weight = 1.0f) + { + const AccumulationT converted_value = static_cast<AccumulationT>(value); + Item &item = accumulation_buffer_[index]; + item.value = converted_value * weight; + item.weight = weight; } void mix_in(const int64_t index, const T &value, const float weight = 1.0f) @@ -291,7 +353,12 @@ class SimpleMixerWithAccumulationType { void finalize() { - for (const int64_t i : buffer_.index_range()) { + this->finalize(buffer_.index_range()); + } + + void finalize(const IndexMask mask) + { + mask.foreach_index([&](const int64_t i) { const Item &item = accumulation_buffer_[i]; if (item.weight > 0.0f) { const float weight_inv = 1.0f / item.weight; @@ -301,7 +368,7 @@ class SimpleMixerWithAccumulationType { else { buffer_[i] = default_value_; } - } + }); } }; @@ -314,8 +381,16 @@ class ColorGeometry4fMixer { public: ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, + IndexMask mask, + ColorGeometry4f default_color = ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + void set(int64_t index, const ColorGeometry4f &color, float weight = 1.0f); void mix_in(int64_t index, const ColorGeometry4f &color, float weight = 1.0f); void finalize(); + void finalize(IndexMask mask); }; class ColorGeometry4bMixer { @@ -328,8 +403,16 @@ class ColorGeometry4bMixer { public: ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255)); + /** + * \param mask: Only initialize these indices. Other indices in the buffer will be invalid. + */ + ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, + IndexMask mask, + ColorGeometry4b default_color = ColorGeometry4b(0, 0, 0, 255)); + void set(int64_t index, const ColorGeometry4b &color, float weight = 1.0f); void mix_in(int64_t index, const ColorGeometry4b &color, float weight = 1.0f); void finalize(); + void finalize(IndexMask mask); }; template<typename T> struct DefaultMixerStruct { @@ -381,12 +464,12 @@ template<> struct DefaultMixerStruct<int8_t> { using type = SimpleMixerWithAccumulationType<int8_t, float, float_to_int8_t>; }; -template<typename T> struct DefaultPropatationMixerStruct { +template<typename T> struct DefaultPropagationMixerStruct { /* Use void by default. This can be checked for in `if constexpr` statements. */ using type = typename DefaultMixerStruct<T>::type; }; -template<> struct DefaultPropatationMixerStruct<bool> { +template<> struct DefaultPropagationMixerStruct<bool> { using type = BooleanPropagationMixer; }; @@ -396,7 +479,7 @@ template<> struct DefaultPropatationMixerStruct<bool> { * (the default mixing for booleans). */ template<typename T> -using DefaultPropatationMixer = typename DefaultPropatationMixerStruct<T>::type; +using DefaultPropagationMixer = typename DefaultPropagationMixerStruct<T>::type; /* Utility to get a good default mixer for a given type. This is `void` when there is no default * mixer for the given type. */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 2067730faca..ee9c7a964d9 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 303 +#define BLENDER_VERSION 304 /* 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 6 +#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_curves.hh b/source/blender/blenkernel/BKE_curves.hh index 568899721a9..fc8a00af4a1 100644 --- a/source/blender/blenkernel/BKE_curves.hh +++ b/source/blender/blenkernel/BKE_curves.hh @@ -97,7 +97,7 @@ class CurvesGeometryRuntime { mutable Span<float3> evaluated_positions_span; /** - * Cache of lengths along each evaluated curve for for each evaluated point. If a curve is + * Cache of lengths along each evaluated curve for each evaluated point. If a curve is * cyclic, it needs one more length value to correspond to the last segment, so in order to * make slicing this array for a curve fast, an extra float is stored for every curve. */ diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 6e27fd2d80f..8bad89a9211 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -11,7 +11,9 @@ #include "BLI_sys_types.h" #include "BLI_utildefines.h" #ifdef __cplusplus +# include "BLI_set.hh" # include "BLI_span.hh" +# include "BLI_string_ref.hh" # include "BLI_vector.hh" #endif @@ -141,6 +143,15 @@ void CustomData_copy(const struct CustomData *source, eCDAllocType alloctype, int totelem); +/** + * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh. + */ +void CustomData_copy_mesh_to_bmesh(const struct CustomData *source, + struct CustomData *dest, + eCustomDataMask mask, + eCDAllocType alloctype, + int totelem); + /* BMESH_TODO, not really a public function but readfile.c needs it */ void CustomData_update_typemap(struct CustomData *data); @@ -155,6 +166,15 @@ bool CustomData_merge(const struct CustomData *source, int totelem); /** + * Like #CustomData_copy but skips copying layers that are stored as flags on #BMesh. + */ +bool CustomData_merge_mesh_to_bmesh(const struct CustomData *source, + struct CustomData *dest, + eCustomDataMask mask, + eCDAllocType alloctype, + int totelem); + +/** * Reallocate custom data to a new element count. * Only affects on data layers which are owned by the CustomData itself, * referenced data is kept unchanged, @@ -697,7 +717,8 @@ void CustomData_data_transfer(const struct MeshPairRemap *me_remap, * the struct. */ void CustomData_blend_write_prepare(CustomData &data, - blender::Vector<CustomDataLayer, 16> &layers_to_write); + blender::Vector<CustomDataLayer, 16> &layers_to_write, + const blender::Set<blender::StringRef> &skip_names = {}); /** * \param layers_to_write: Layers created by #CustomData_blend_write_prepare. diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index 7a21e85e310..cdca740555a 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -61,7 +61,6 @@ typedef struct DispList { int totindex; /* indexed array drawing surfaces */ } DispList; -void BKE_displist_copy(struct ListBase *lbn, const struct ListBase *lb); DispList *BKE_displist_find(struct ListBase *lb, int type); void BKE_displist_normals_add(struct ListBase *lb); void BKE_displist_count(const struct ListBase *lb, int *totvert, int *totface, int *tottri); diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index be2ec3e3dca..a5c4d5e1365 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -505,7 +505,7 @@ class CurveComponentLegacy : public GeometryComponent { /** * A geometry component that stores a group of curves, corresponding the #Curves data-block type - * and the #CurvesGeometry type. Attributes are are stored on the control point domain and the + * and the #CurvesGeometry type. Attributes are stored on the control point domain and the * curve domain. */ class CurveComponent : public GeometryComponent { diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 404ce63a5df..c14da538e7c 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -7,6 +7,7 @@ */ #include "BLI_compiler_attrs.h" +#include "BLI_sys_types.h" #ifdef __cplusplus extern "C" { diff --git a/source/blender/blenkernel/BKE_idprop.hh b/source/blender/blenkernel/BKE_idprop.hh index 1e741cc252f..6a42ab1669f 100644 --- a/source/blender/blenkernel/BKE_idprop.hh +++ b/source/blender/blenkernel/BKE_idprop.hh @@ -45,6 +45,9 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, d std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, const StringRefNull value); +/** \brief Allocate a new IDProperty of type IDP_ID, set its name and value. */ +std::unique_ptr<IDProperty, IDPropertyDeleter> create(StringRefNull prop_name, ID *id); + /** * \brief Allocate a new IDProperty of type IDP_ARRAY and subtype IDP_INT. * diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 04ea94cbfb4..95f4c8f6dce 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -113,7 +113,7 @@ typedef struct IDTypeInfo { */ short id_code; /** - * Bitflag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's + * Bit-flag matching id_code, used for filtering (e.g. in file browser), see DNA_ID.h's * FILTER_ID_XX enums. */ uint64_t id_filter; diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 2db8f75770a..eb43ce823ac 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -366,14 +366,7 @@ bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile); void BKE_image_reassign_tile(struct Image *ima, struct ImageTile *tile, int new_tile_number); void BKE_image_sort_tiles(struct Image *ima); -bool BKE_image_fill_tile(struct Image *ima, - struct ImageTile *tile, - int width, - int height, - const float color[4], - int gen_type, - int planes, - bool is_float); +bool BKE_image_fill_tile(struct Image *ima, struct ImageTile *tile); typedef enum { UDIM_TILE_FORMAT_NONE = 0, @@ -425,13 +418,13 @@ int BKE_image_get_tile_from_pos(struct Image *ima, void BKE_image_get_tile_uv(const struct Image *ima, const int tile_number, float r_uv[2]); /** - * Return the tile_number for the closest UDIM tile. + * Return the tile_number for the closest UDIM tile to `co`. */ int BKE_image_find_nearest_tile_with_offset(const struct Image *image, const float co[2], - float r_uv_offset[2]) ATTR_NONNULL(1, 2, 3); + float r_uv_offset[2]) ATTR_NONNULL(2, 3); int BKE_image_find_nearest_tile(const struct Image *image, const float co[2]) - ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; + ATTR_NONNULL(2) ATTR_WARN_UNUSED_RESULT; void BKE_image_get_size(struct Image *image, struct ImageUser *iuser, int *r_width, int *r_height); void BKE_image_get_size_fl(struct Image *image, struct ImageUser *iuser, float r_size[2]); diff --git a/source/blender/blenkernel/BKE_key.h b/source/blender/blenkernel/BKE_key.h index 37b30d63722..9f506ded8e9 100644 --- a/source/blender/blenkernel/BKE_key.h +++ b/source/blender/blenkernel/BKE_key.h @@ -47,7 +47,7 @@ void key_curve_normal_weights(float t, float data[4], int type); /** * Returns key coordinates (+ tilt) when key applied, NULL otherwise. * - * \param obdata if given, also update that geometry with the result of the shape keys evaluation. + * \param obdata: if given, also update that geometry with the result of the shape keys evaluation. */ float *BKE_key_evaluate_object_ex( struct Object *ob, int *r_totelem, float *arr, size_t arr_size, struct ID *obdata); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 94497d9a487..febdad2ca0d 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -444,9 +444,9 @@ struct ID *BKE_id_copy(struct Main *bmain, const struct ID *id); * Currently, it only handles the given ID, and their shape keys and actions if any, according to * the given `duplicate_flags`. * - * \param duplicate_flags is of type #eDupli_ID_Flags, see #UserDef.dupflag. Currently only + * \param duplicate_flags: is of type #eDupli_ID_Flags, see #UserDef.dupflag. Currently only * `USER_DUP_LINKED_ID` and `USER_DUP_ACT` have an effect here. - * \param copy_flags flags passed to #BKE_id_copy_ex. + * \param copy_flags: flags passed to #BKE_id_copy_ex. */ struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, struct ID *id, @@ -454,6 +454,13 @@ struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, int copy_flags); /** + * Special version of #BKE_id_copy which is safe from using evaluated id as source with a copy + * result appearing in the main database. + * Takes care of the referenced data-blocks consistency. + */ +struct ID *BKE_id_copy_for_use_in_bmain(struct Main *bmain, const struct ID *id); + +/** * Does a mere memory swap over the whole IDs data (including type-specific memory). * \note Most internal ID data itself is not swapped (only IDProperties are). * diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 17f541b362e..b9f6b4b73f3 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -853,7 +853,7 @@ struct Mesh *BKE_mesh_merge_verts(struct Mesh *mesh, int merge_mode); /** - * Account for custom-data such as UV's becoming detached because of of imprecision + * Account for custom-data such as UV's becoming detached because of imprecision * in custom-data interpolation. * Without running this operation subdivision surface can cause UV's to be disconnected, * see: T81065. @@ -865,19 +865,7 @@ void BKE_mesh_merge_customdata_for_apply_modifier(struct Mesh *me); /** * Update the hide flag for edges and faces from the corresponding flag in verts. */ -void BKE_mesh_flush_hidden_from_verts_ex(const struct MVert *mvert, - const struct MLoop *mloop, - struct MEdge *medge, - int totedge, - struct MPoly *mpoly, - int totpoly); void BKE_mesh_flush_hidden_from_verts(struct Mesh *me); -void BKE_mesh_flush_hidden_from_polys_ex(struct MVert *mvert, - const struct MLoop *mloop, - struct MEdge *medge, - int totedge, - const struct MPoly *mpoly, - int totpoly); void BKE_mesh_flush_hidden_from_polys(struct Mesh *me); /** * simple poly -> vert/edge selection. diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.h b/source/blender/blenkernel/BKE_mesh_legacy_convert.h index 909fd0e0dea..bbc61d5af5e 100644 --- a/source/blender/blenkernel/BKE_mesh_legacy_convert.h +++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.h @@ -18,6 +18,16 @@ struct Mesh; struct MFace; /** + * Convert the hidden element attributes to the old flag format for writing. + */ +void BKE_mesh_legacy_convert_hide_layers_to_flags(struct Mesh *mesh); +/** + * Convert the old hide flags (#ME_HIDE) to the hidden element attribute for reading. + * Only add the attributes when there are any elements in each domain hidden. + */ +void BKE_mesh_legacy_convert_flags_to_hide_layers(struct Mesh *mesh); + +/** * Recreate #MFace Tessellation. * * \note This doesn't use multi-threading like #BKE_mesh_recalc_looptri since diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index c58bcbea242..abe590b6806 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -17,11 +17,10 @@ struct MLoopUV; struct MPoly; struct MVert; -/* map from uv vertex to face (for select linked, stitch, uv suburf) */ - /* UvVertMap */ #define STD_UV_CONNECT_LIMIT 0.0001f +/* Map from uv vertex to face. Used by select linked, uv subdivision-surface and obj exporter. */ typedef struct UvVertMap { struct UvMapVert **vert; struct UvMapVert *buf; @@ -52,21 +51,37 @@ typedef struct UvElement { unsigned int island; } UvElement; -/* UvElementMap is a container for UvElements of a mesh. It stores some UvElements belonging to the - * same uv island in sequence and the number of uvs per island so it is possible to access all uvs - * belonging to an island directly by iterating through the buffer. +/** UvElementMap is a container for UvElements of a BMesh. + * + * It simplifies access to UV information and ensures the + * different UV selection modes are respected. + * + * If islands are calculated, it also stores UvElements + * belonging to the same uv island in sequence and + * the number of uvs per island. */ typedef struct UvElementMap { - /* address UvElements by their vertex */ - struct UvElement **vert; - /* UvElement Store */ - struct UvElement *buf; - /* Total number of UVs in the layer. Useful to know */ - int totalUVs; - /* Number of Islands in the mesh */ - int totalIslands; - /* Stores the starting index in buf where each island begins */ - int *islandIndices; + /** UvElement Storage. */ + struct UvElement *storage; + /** Total number of UVs. */ + int total_uvs; + /** Total number of unique UVs. */ + int total_unique_uvs; + + /** If Non-NULL, address UvElements by `BM_elem_index_get(BMVert*)`. */ + struct UvElement **vertex; + + /** If Non-NULL, pointer to local head of each unique UV. */ + struct UvElement **head_table; + + /** Number of islands, or zero if not calculated. */ + int total_islands; + /** Array of starting index in #storage where each island begins. */ + int *island_indices; + /** Array of number of UVs in each island. */ + int *island_total_uvs; + /** Array of number of unique UVs in each island. */ + int *island_total_unique_uvs; } UvElementMap; /* Connectivity data */ @@ -77,6 +92,7 @@ typedef struct MeshElemMap { /* mapping */ UvVertMap *BKE_mesh_uv_vert_map_create(const struct MPoly *mpoly, + const bool *hide_poly, const struct MLoop *mloop, const struct MLoopUV *mloopuv, unsigned int totpoly, diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index 0b7d1a1835f..fb01083f334 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -13,7 +13,6 @@ #include "DNA_meshdata_types.h" #include "BKE_attribute.h" -#include "BKE_attribute.hh" struct Mesh; struct BVHTreeFromMesh; @@ -27,22 +26,22 @@ namespace blender::bke::mesh_surface_sample { void sample_point_attribute(const Mesh &mesh, Span<int> looptri_indices, Span<float3> bary_coords, - const GVArray &data_in, - const IndexMask mask, - GMutableSpan data_out); + const GVArray &src, + IndexMask mask, + GMutableSpan dst); void sample_corner_attribute(const Mesh &mesh, Span<int> looptri_indices, Span<float3> bary_coords, - const GVArray &data_in, - const IndexMask mask, - GMutableSpan data_out); + const GVArray &src, + IndexMask mask, + GMutableSpan dst); void sample_face_attribute(const Mesh &mesh, Span<int> looptri_indices, - const GVArray &data_in, - const IndexMask mask, - GMutableSpan data_out); + const GVArray &src, + IndexMask mask, + GMutableSpan dst); enum class eAttributeMapMode { INTERPOLATED, @@ -57,7 +56,6 @@ enum class eAttributeMapMode { * these are computed lazily when needed and re-used. */ class MeshAttributeInterpolator { - private: const Mesh *mesh_; const IndexMask mask_; const Span<float3> positions_; @@ -68,18 +66,14 @@ class MeshAttributeInterpolator { public: MeshAttributeInterpolator(const Mesh *mesh, - const IndexMask mask, - const Span<float3> positions, - const Span<int> looptri_indices); + IndexMask mask, + Span<float3> positions, + Span<int> looptri_indices); void sample_data(const GVArray &src, eAttrDomain domain, eAttributeMapMode mode, - const GMutableSpan dst); - - void sample_attribute(const GAttributeReader &src_attribute, - GSpanAttributeWriter &dst_attribute, - eAttributeMapMode mode); + GMutableSpan dst); protected: Span<float3> ensure_barycentric_coords(); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index 90dbec7ec52..b42b9df510d 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -101,6 +101,7 @@ typedef struct bNodeSocketTemplate { namespace blender { class CPPType; namespace nodes { +class DNode; class NodeMultiFunctionBuilder; class GeoNodeExecParams; class NodeDeclarationBuilder; @@ -109,6 +110,11 @@ class GatherLinkSearchOpParams; namespace fn { class MFDataType; } // namespace fn +namespace realtime_compositor { +class Context; +class NodeOperation; +class ShaderNode; +} // namespace realtime_compositor } // namespace blender using CPPTypeHandle = blender::CPPType; @@ -123,7 +129,14 @@ using SocketGetGeometryNodesCPPValueFunction = void (*)(const struct bNodeSocket using NodeGatherSocketLinkOperationsFunction = void (*)(blender::nodes::GatherLinkSearchOpParams ¶ms); +using NodeGetCompositorOperationFunction = blender::realtime_compositor::NodeOperation + *(*)(blender::realtime_compositor::Context &context, blender::nodes::DNode node); +using NodeGetCompositorShaderNodeFunction = + blender::realtime_compositor::ShaderNode *(*)(blender::nodes::DNode node); + #else +typedef void *NodeGetCompositorOperationFunction; +typedef void *NodeGetCompositorShaderNodeFunction; typedef void *NodeMultiFunctionBuildFunction; typedef void *NodeGeometryExecFunction; typedef void *NodeDeclareFunction; @@ -309,6 +322,14 @@ typedef struct bNodeType { /* gpu */ NodeGPUExecFunction gpu_fn; + /* Get an instance of this node's compositor operation. Freeing the instance is the + * responsibility of the caller. */ + NodeGetCompositorOperationFunction get_compositor_operation; + + /* Get an instance of this node's compositor shader node. Freeing the instance is the + * responsibility of the caller. */ + NodeGetCompositorShaderNodeFunction get_compositor_shader_node; + /* Build a multi-function for this node. */ NodeMultiFunctionBuildFunction build_multi_function; @@ -374,6 +395,9 @@ typedef struct bNodeTreeType { int type; /* type identifier */ char idname[64]; /* identifier name */ + /* The ID name of group nodes for this type. */ + char group_idname[64]; + char ui_name[64]; char ui_description[256]; int ui_icon; diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index f0eb16a819d..8f3b488c7db 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -586,7 +586,6 @@ void BKE_object_runtime_reset_on_copy(struct Object *object, int flag); void BKE_object_runtime_free_data(struct Object *object); void BKE_object_batch_cache_dirty_tag(struct Object *ob); -void BKE_object_data_batch_cache_dirty_tag(struct ID *object_data); /* this function returns a superset of the scenes selection based on relationships */ diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 162459d2005..fa67ff08383 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -13,6 +13,7 @@ #include "DNA_object_enums.h" #include "BKE_attribute.h" +#include "BKE_pbvh.h" #ifdef __cplusplus extern "C" { @@ -212,7 +213,7 @@ bool BKE_paint_always_hide_test(struct Object *ob); * Returns non-zero if any of the face's vertices are hidden, zero otherwise. */ bool paint_is_face_hidden(const struct MLoopTri *lt, - const struct MVert *mvert, + const bool *hide_vert, const struct MLoop *mloop); /** * Returns non-zero if any of the corners of the grid @@ -397,7 +398,7 @@ typedef struct SculptVertexInfo { typedef struct SculptBoundaryEditInfo { /* Vertex index from where the topology propagation reached this vertex. */ - int original_vertex; + int original_vertex_i; /* How many steps were needed to reach this vertex from the boundary. */ int num_propagation_steps; @@ -408,13 +409,14 @@ typedef struct SculptBoundaryEditInfo { /* Edge for drawing the boundary preview in the cursor. */ typedef struct SculptBoundaryPreviewEdge { - int v1; - int v2; + PBVHVertRef v1; + PBVHVertRef v2; } SculptBoundaryPreviewEdge; typedef struct SculptBoundary { /* Vertex indices of the active boundary. */ - int *vertices; + PBVHVertRef *vertices; + int *vertices_i; int vertices_capacity; int num_vertices; @@ -432,12 +434,13 @@ typedef struct SculptBoundary { bool forms_loop; /* Initial vertex in the boundary which is closest to the current sculpt active vertex. */ - int initial_vertex; + PBVHVertRef initial_vertex; + int initial_vertex_i; /* Vertex that at max_propagation_steps from the boundary and closest to the original active * vertex that was used to initialize the boundary. This is used as a reference to check how much * the deformation will go into the mesh and to calculate the strength of the brushes. */ - int pivot_vertex; + PBVHVertRef pivot_vertex; /* Stores the initial positions of the pivot and boundary initial vertex as they may be deformed * during the brush action. This allows to use them as a reference positions and vectors for some @@ -565,7 +568,7 @@ typedef struct SculptSession { struct ExpandCache *expand_cache; /* Cursor data and active vertex for tools */ - int active_vertex_index; + PBVHVertRef active_vertex; int active_face_index; int active_grid_index; @@ -591,8 +594,8 @@ typedef struct SculptSession { struct Scene *scene; /* Dynamic mesh preview */ - int *preview_vert_index_list; - int preview_vert_index_count; + PBVHVertRef *preview_vert_list; + int preview_vert_count; /* Pose Brush Preview */ float pose_origin[3]; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 5641ff0ba34..2be3f323d07 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -8,8 +8,11 @@ */ #include "BLI_bitmap.h" +#include "BLI_compiler_compat.h" #include "BLI_ghash.h" +#include "bmesh.h" + /* For embedding CCGKey in iterator. */ #include "BKE_attribute.h" #include "BKE_ccg.h" @@ -43,6 +46,41 @@ struct MeshElemMap; typedef struct PBVH PBVH; typedef struct PBVHNode PBVHNode; +typedef enum { + PBVH_FACES, + PBVH_GRIDS, + PBVH_BMESH, +} PBVHType; + +/* Public members of PBVH, used for inlined functions. */ +struct PBVHPublic { + PBVHType type; + BMesh *bm; +}; + +/* + * These structs represent logical verts/edges/faces. + * for PBVH_GRIDS and PBVH_FACES they store integer + * offsets, PBVH_BMESH stores pointers. + * + * The idea is to enforce stronger type checking by encapsulating + * intptr_t's in structs. + */ + +typedef struct PBVHVertRef { + intptr_t i; +} PBVHVertRef; + +typedef struct PBVHEdgeRef { + intptr_t i; +} PBVHEdgeRef; + +typedef struct PBVHFaceRef { + intptr_t i; +} PBVHFaceRef; + +#define PBVH_REF_NONE -1LL + typedef struct { float (*co)[3]; } PBVHProxyNode; @@ -87,9 +125,97 @@ typedef struct PBVHFrustumPlanes { int num_planes; } PBVHFrustumPlanes; +BLI_INLINE PBVHType BKE_pbvh_type(const PBVH *pbvh) +{ + return ((const struct PBVHPublic *)pbvh)->type; +} + +BLI_INLINE BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh) +{ + return ((struct PBVHPublic *)pbvh)->bm; +} + void BKE_pbvh_set_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes); void BKE_pbvh_get_frustum_planes(PBVH *pbvh, PBVHFrustumPlanes *planes); +BLI_INLINE PBVHVertRef BKE_pbvh_make_vref(intptr_t i) +{ + PBVHVertRef ret = {i}; + return ret; +} + +BLI_INLINE PBVHEdgeRef BKE_pbvh_make_eref(intptr_t i) +{ + PBVHEdgeRef ret = {i}; + return ret; +} + +BLI_INLINE PBVHFaceRef BKE_pbvh_make_fref(intptr_t i) +{ + PBVHFaceRef ret = {i}; + return ret; +} + +BLI_INLINE int BKE_pbvh_vertex_to_index(PBVH *pbvh, PBVHVertRef v) +{ + return (BKE_pbvh_type(pbvh) == PBVH_BMESH && v.i != PBVH_REF_NONE ? + BM_elem_index_get((BMVert *)(v.i)) : + (v.i)); +} + +BLI_INLINE PBVHVertRef BKE_pbvh_index_to_vertex(PBVH *pbvh, int index) +{ + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: + case PBVH_GRIDS: + return BKE_pbvh_make_vref(index); + case PBVH_BMESH: + return BKE_pbvh_make_vref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->vtable[index]); + } + + return BKE_pbvh_make_vref(PBVH_REF_NONE); +} + +BLI_INLINE int BKE_pbvh_edge_to_index(PBVH *pbvh, PBVHEdgeRef e) +{ + return (BKE_pbvh_type(pbvh) == PBVH_BMESH && e.i != PBVH_REF_NONE ? + BM_elem_index_get((BMEdge *)(e.i)) : + (e.i)); +} + +BLI_INLINE PBVHEdgeRef BKE_pbvh_index_to_edge(PBVH *pbvh, int index) +{ + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: + case PBVH_GRIDS: + return BKE_pbvh_make_eref(index); + case PBVH_BMESH: + return BKE_pbvh_make_eref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->etable[index]); + } + + return BKE_pbvh_make_eref(PBVH_REF_NONE); +} + +BLI_INLINE int BKE_pbvh_face_to_index(PBVH *pbvh, PBVHFaceRef f) +{ + return (BKE_pbvh_type(pbvh) == PBVH_BMESH && f.i != PBVH_REF_NONE ? + BM_elem_index_get((BMFace *)(f.i)) : + (f.i)); +} + +BLI_INLINE PBVHFaceRef BKE_pbvh_index_to_face(PBVH *pbvh, int index) +{ + switch (BKE_pbvh_type(pbvh)) { + case PBVH_FACES: + case PBVH_GRIDS: + return BKE_pbvh_make_fref(index); + case PBVH_BMESH: + return BKE_pbvh_make_fref((intptr_t)BKE_pbvh_get_bmesh(pbvh)->ftable[index]); + } + + return BKE_pbvh_make_fref(PBVH_REF_NONE); +} + /* Callbacks */ /** @@ -181,7 +307,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, float *depth, - int *active_vertex_index, + PBVHVertRef *active_vertex, int *active_face_grid_index, float *face_normal); @@ -230,13 +356,7 @@ void BKE_pbvh_draw_debug_cb( void *user_data); /* PBVH Access */ -typedef enum { - PBVH_FACES, - PBVH_GRIDS, - PBVH_BMESH, -} PBVHType; -PBVHType BKE_pbvh_type(const PBVH *pbvh); bool BKE_pbvh_has_faces(const PBVH *pbvh); /** @@ -272,7 +392,6 @@ int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh); /** * Only valid for type == #PBVH_BMESH. */ -struct BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh); void BKE_pbvh_bmesh_detail_size_set(PBVH *pbvh, float detail_size); typedef enum { @@ -308,7 +427,7 @@ void BKE_pbvh_node_fully_unmasked_set(PBVHNode *node, int fully_masked); bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node); void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh); -void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, int index); +void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex); void BKE_pbvh_node_get_grids(PBVH *pbvh, PBVHNode *node, @@ -399,6 +518,7 @@ typedef struct PBVHVertexIter { int gy; int i; int index; + PBVHVertRef vertex; bool respect_hide; /* grid */ @@ -413,6 +533,7 @@ typedef struct PBVHVertexIter { /* mesh */ struct MVert *mverts; float (*vert_normals)[3]; + const bool *hide_vert; int totvert; const int *vert_indices; float *vmask; @@ -443,7 +564,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m if (vi.grids) { \ vi.width = vi.gridsize; \ vi.height = vi.gridsize; \ - vi.index = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \ + vi.index = vi.vertex.i = vi.grid_indices[vi.g] * vi.key.grid_area - 1; \ vi.grid = vi.grids[vi.grid_indices[vi.g]]; \ if (mode == PBVH_ITER_UNIQUE) { \ vi.gh = vi.grid_hidden[vi.grid_indices[vi.g]]; \ @@ -462,6 +583,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi.mask = vi.key.has_mask ? CCG_elem_mask(&vi.key, vi.grid) : NULL; \ vi.grid = CCG_elem_next(&vi.key, vi.grid); \ vi.index++; \ + vi.vertex.i++; \ vi.visible = true; \ if (vi.gh) { \ if (BLI_BITMAP_TEST(vi.gh, vi.gy * vi.gridsize + vi.gx)) { \ @@ -472,7 +594,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m else if (vi.mverts) { \ vi.mvert = &vi.mverts[vi.vert_indices[vi.gx]]; \ if (vi.respect_hide) { \ - vi.visible = !(vi.mvert->flag & ME_HIDE); \ + vi.visible = !(vi.hide_vert && vi.hide_vert[vi.vert_indices[vi.gx]]); \ if (mode == PBVH_ITER_UNIQUE && !vi.visible) { \ continue; \ } \ @@ -482,7 +604,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m } \ vi.co = vi.mvert->co; \ vi.no = vi.vert_normals[vi.vert_indices[vi.gx]]; \ - vi.index = vi.vert_indices[vi.i]; \ + vi.index = vi.vertex.i = vi.vert_indices[vi.i]; \ if (vi.vmask) { \ vi.mask = &vi.vmask[vi.index]; \ } \ @@ -502,6 +624,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m } \ vi.co = vi.bm_vert->co; \ vi.fno = vi.bm_vert->no; \ + vi.vertex = BKE_pbvh_make_vref((intptr_t)vi.bm_vert); \ vi.index = BM_elem_index_get(vi.bm_vert); \ vi.mask = (float *)BM_ELEM_CD_GET_VOID_P(vi.bm_vert, vi.cd_vert_mask_offset); \ } @@ -545,6 +668,8 @@ void BKE_pbvh_parallel_range_settings(struct TaskParallelSettings *settings, struct MVert *BKE_pbvh_get_verts(const PBVH *pbvh); const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3]; +const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh); +bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh); PBVHColorBufferNode *BKE_pbvh_node_color_buffer_get(PBVHNode *node); void BKE_pbvh_node_color_buffer_free(PBVH *pbvh); @@ -581,8 +706,8 @@ void BKE_pbvh_node_num_loops(PBVH *pbvh, PBVHNode *node, int *r_totloop); void BKE_pbvh_update_active_vcol(PBVH *pbvh, const struct Mesh *mesh); void BKE_pbvh_pmap_set(PBVH *pbvh, const struct MeshElemMap *pmap); -void BKE_pbvh_vertex_color_set(PBVH *pbvh, int vertex, const float color[4]); -void BKE_pbvh_vertex_color_get(const PBVH *pbvh, int vertex, float r_color[4]); +void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color[4]); +void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_color[4]); void BKE_pbvh_ensure_node_loops(PBVH *pbvh); bool BKE_pbvh_draw_cache_invalid(const PBVH *pbvh); diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 3922bfb6c0d..a24a3e05dab 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -291,7 +291,7 @@ enum { /* Draw an item in the uiList */ typedef void (*uiListDrawItemFunc)(struct uiList *ui_list, - struct bContext *C, + const struct bContext *C, struct uiLayout *layout, struct PointerRNA *dataptr, struct PointerRNA *itemptr, @@ -303,12 +303,12 @@ typedef void (*uiListDrawItemFunc)(struct uiList *ui_list, /* Draw the filtering part of an uiList */ typedef void (*uiListDrawFilterFunc)(struct uiList *ui_list, - struct bContext *C, + const struct bContext *C, struct uiLayout *layout); /* Filter items of an uiList */ typedef void (*uiListFilterItemsFunc)(struct uiList *ui_list, - struct bContext *C, + const struct bContext *C, struct PointerRNA *, const char *propname); diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 767018ae0bb..27542aa3586 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -360,7 +360,7 @@ class BezierSpline final : public Spline { * Returns non-owning access to an array of values containing the information necessary to * interpolate values from the original control points to evaluated points. The control point * index is the integer part of each value, and the factor used for interpolating to the next - * control point is the remaining factional part. + * control point is the remaining fractional part. */ blender::Span<float> evaluated_mappings() const; blender::Span<blender::float3> evaluated_positions() const final; diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index f76f7f5a968..e3f00d03a3b 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -191,7 +191,7 @@ set(SRC intern/mask_evaluate.c intern/mask_rasterize.c intern/material.c - intern/mball.c + intern/mball.cc intern/mball_tessellate.c intern/mesh.cc intern/mesh_boolean_convert.cc diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index a29d8726f21..2ce5863c176 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -1595,7 +1595,7 @@ static void editbmesh_calc_modifiers(struct Depsgraph *depsgraph, /* Add orco coordinates to final and deformed mesh if requested. */ if (final_datamask.vmask & CD_MASK_ORCO) { - /* FIXME(Campbell): avoid the need to convert to mesh data just to add an orco layer. */ + /* FIXME(@campbellbarton): avoid the need to convert to mesh data just to add an orco layer. */ BKE_mesh_wrapper_ensure_mdata(mesh_final); add_orco_mesh(ob, em_input, mesh_final, mesh_orco, CD_ORCO); diff --git a/source/blender/blenkernel/intern/armature_update.c b/source/blender/blenkernel/intern/armature_update.c index 2db4c086e04..6d7aed239e7 100644 --- a/source/blender/blenkernel/intern/armature_update.c +++ b/source/blender/blenkernel/intern/armature_update.c @@ -288,7 +288,7 @@ static int position_tail_on_spline(bSplineIKConstraint *ik_data, int max_seg_idx = BKE_anim_path_get_array_size(cache) - 1; /* Make an initial guess of where our intersection point will be. - * If the curve was a straight line, then the faction passed in r_new_curve_pos + * If the curve was a straight line, then the fraction passed in r_new_curve_pos * would be the correct location. * So make it our first initial guess. */ diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 1af3cde1821..b9995796a21 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -56,7 +56,7 @@ const char *no_procedural_access_message = bool allow_procedural_attribute_access(StringRef attribute_name) { - return !attribute_name.startswith(".selection"); + return !attribute_name.startswith(".selection") && !attribute_name.startswith(".hide"); } static int attribute_data_type_complexity(const eCustomDataType data_type) diff --git a/source/blender/blenkernel/intern/attribute_math.cc b/source/blender/blenkernel/intern/attribute_math.cc index c38df2a2969..d8102b4eeb8 100644 --- a/source/blender/blenkernel/intern/attribute_math.cc +++ b/source/blender/blenkernel/intern/attribute_math.cc @@ -4,13 +4,31 @@ namespace blender::attribute_math { -ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> output_buffer, +ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, ColorGeometry4f default_color) - : buffer_(output_buffer), - default_color_(default_color), - total_weights_(output_buffer.size(), 0.0f) + : ColorGeometry4fMixer(buffer, buffer.index_range(), default_color) +{ +} + +ColorGeometry4fMixer::ColorGeometry4fMixer(MutableSpan<ColorGeometry4f> buffer, + const IndexMask mask, + const ColorGeometry4f default_color) + : buffer_(buffer), default_color_(default_color), total_weights_(buffer.size(), 0.0f) { - buffer_.fill(ColorGeometry4f(0.0f, 0.0f, 0.0f, 0.0f)); + const ColorGeometry4f zero{0.0f, 0.0f, 0.0f, 0.0f}; + mask.foreach_index([&](const int64_t i) { buffer_[i] = zero; }); +} + +void ColorGeometry4fMixer::set(const int64_t index, + const ColorGeometry4f &color, + const float weight) +{ + BLI_assert(weight >= 0.0f); + buffer_[index].r = color.r * weight; + buffer_[index].g = color.g * weight; + buffer_[index].b = color.b * weight; + buffer_[index].a = color.a * weight; + total_weights_[index] = weight; } void ColorGeometry4fMixer::mix_in(const int64_t index, @@ -28,7 +46,12 @@ void ColorGeometry4fMixer::mix_in(const int64_t index, void ColorGeometry4fMixer::finalize() { - for (const int64_t i : buffer_.index_range()) { + this->finalize(buffer_.index_range()); +} + +void ColorGeometry4fMixer::finalize(const IndexMask mask) +{ + mask.foreach_index([&](const int64_t i) { const float weight = total_weights_[i]; ColorGeometry4f &output_color = buffer_[i]; if (weight > 0.0f) { @@ -41,16 +64,37 @@ void ColorGeometry4fMixer::finalize() else { output_color = default_color_; } - } + }); } ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, - ColorGeometry4b default_color) + const ColorGeometry4b default_color) + : ColorGeometry4bMixer(buffer, buffer.index_range(), default_color) +{ +} + +ColorGeometry4bMixer::ColorGeometry4bMixer(MutableSpan<ColorGeometry4b> buffer, + const IndexMask mask, + const ColorGeometry4b default_color) : buffer_(buffer), default_color_(default_color), total_weights_(buffer.size(), 0.0f), accumulation_buffer_(buffer.size(), float4(0, 0, 0, 0)) { + const ColorGeometry4b zero{0, 0, 0, 0}; + mask.foreach_index([&](const int64_t i) { buffer_[i] = zero; }); +} + +void ColorGeometry4bMixer::ColorGeometry4bMixer::set(int64_t index, + const ColorGeometry4b &color, + const float weight) +{ + BLI_assert(weight >= 0.0f); + accumulation_buffer_[index][0] = color.r * weight; + accumulation_buffer_[index][1] = color.g * weight; + accumulation_buffer_[index][2] = color.b * weight; + accumulation_buffer_[index][3] = color.a * weight; + total_weights_[index] = weight; } void ColorGeometry4bMixer::mix_in(int64_t index, const ColorGeometry4b &color, float weight) @@ -66,7 +110,12 @@ void ColorGeometry4bMixer::mix_in(int64_t index, const ColorGeometry4b &color, f void ColorGeometry4bMixer::finalize() { - for (const int64_t i : buffer_.index_range()) { + this->finalize(buffer_.index_range()); +} + +void ColorGeometry4bMixer::finalize(const IndexMask mask) +{ + mask.foreach_index([&](const int64_t i) { const float weight = total_weights_[i]; const float4 &accum_value = accumulation_buffer_[i]; ColorGeometry4b &output_color = buffer_[i]; @@ -80,7 +129,7 @@ void ColorGeometry4bMixer::finalize() else { output_color = default_color_; } - } + }); } } // namespace blender::attribute_math diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 5838ef1cbbe..fc45ce0bbe7 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -597,7 +597,7 @@ using eGPCurveMappingPreset = enum eGPCurveMappingPreset { GPCURVE_PRESET_CHISEL_STRENGTH = 5, }; -static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, int preset) +static void brush_gpencil_curvemap_reset(CurveMap *cuma, int tot, eGPCurveMappingPreset preset) { if (cuma->curve) { MEM_freeN(cuma->curve); diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 03dd5c89b70..d0b57b45d35 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -27,6 +27,8 @@ #include "MEM_guardedalloc.h" +using blender::VArray; + /* -------------------------------------------------------------------- */ /** \name BVHCache * \{ */ @@ -1181,9 +1183,13 @@ static BLI_bitmap *loose_edges_map_get(const MEdge *medge, } static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly, + const VArray<bool> &hide_poly, const int looptri_len, int *r_looptri_active_len) { + if (hide_poly.is_single() && !hide_poly.get_internal_single()) { + return nullptr; + } BLI_bitmap *looptri_mask = BLI_BITMAP_NEW(looptri_len, __func__); int looptri_no_hidden_len = 0; @@ -1191,8 +1197,7 @@ static BLI_bitmap *looptri_no_hidden_map_get(const MPoly *mpoly, int i_poly = 0; while (looptri_iter != looptri_len) { int mp_totlooptri = mpoly[i_poly].totloop - 2; - const MPoly &mp = mpoly[i_poly]; - if (mp.flag & ME_HIDE) { + if (hide_poly[i_poly]) { looptri_iter += mp_totlooptri; } else { @@ -1276,9 +1281,15 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, 0.0f, tree_type, 6, mesh->mvert, mesh->mface, mesh->totface, nullptr, -1); break; - case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: - mask = looptri_no_hidden_map_get(mesh->mpoly, looptri_len, &mask_bits_act_len); + case BVHTREE_FROM_LOOPTRI_NO_HIDDEN: { + blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*mesh); + mask = looptri_no_hidden_map_get( + mesh->mpoly, + attributes.lookup_or_default(".hide_poly", ATTR_DOMAIN_FACE, false), + looptri_len, + &mask_bits_act_len); ATTR_FALLTHROUGH; + } case BVHTREE_FROM_LOOPTRI: data->tree = bvhtree_from_mesh_looptri_create_tree(0.0f, tree_type, diff --git a/source/blender/blenkernel/intern/colorband.c b/source/blender/blenkernel/intern/colorband.c index b2f817b7821..5145f1cbbc0 100644 --- a/source/blender/blenkernel/intern/colorband.c +++ b/source/blender/blenkernel/intern/colorband.c @@ -144,7 +144,7 @@ static float color_sample_remove_cost(const struct ColorResampleElem *c) return area; } -/* TODO(campbell): create BLI_math_filter? */ +/* TODO(@campbellbarton): create `BLI_math_filter` ? */ static float filter_gauss(float x) { const float gaussfac = 1.6f; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index 9f1d2d7bbb2..071fa5fe6bf 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1559,7 +1559,7 @@ static void followpath_evaluate(bConstraint *con, bConstraintOb *cob, ListBase * /* un-apply scaling caused by path */ if ((data->followflag & FOLLOWPATH_RADIUS) == 0) { - /* XXX(campbell): Assume that scale correction means that radius + /* XXX(@campbellbarton): Assume that scale correction means that radius * will have some scale error in it. */ float obsize[3]; diff --git a/source/blender/blenkernel/intern/crazyspace.cc b/source/blender/blenkernel/intern/crazyspace.cc index 8d4b64da817..fdd269bd9c8 100644 --- a/source/blender/blenkernel/intern/crazyspace.cc +++ b/source/blender/blenkernel/intern/crazyspace.cc @@ -73,7 +73,7 @@ static void set_crazy_vertex_quat(float r_quat[4], static bool modifiers_disable_subsurf_temporary(struct Scene *scene, Object *ob) { bool disabled = false; - int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, 1); + int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, true); ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first); for (int i = 0; md && i <= cageIndex; i++, md = md->next) { @@ -241,7 +241,7 @@ int BKE_crazyspace_get_first_deform_matrices_editbmesh(struct Depsgraph *depsgra Mesh *me_input = static_cast<Mesh *>(ob->data); Mesh *me = nullptr; int i, a, modifiers_left_num = 0, verts_num = 0; - int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, 1); + int cageIndex = BKE_modifiers_get_cage_index(scene, ob, nullptr, true); float(*defmats)[3][3] = nullptr, (*deformedVerts)[3] = nullptr; VirtualModifierData virtualModifierData; ModifierEvalContext mectx = {depsgraph, ob, ModifierApplyFlag(0)}; @@ -362,7 +362,7 @@ int BKE_sculpt_get_first_deform_matrices(struct Depsgraph *depsgraph, VirtualModifierData virtualModifierData; Object object_eval; crazyspace_init_object_for_eval(depsgraph, object, &object_eval); - MultiresModifierData *mmd = get_multires_modifier(scene, &object_eval, 0); + MultiresModifierData *mmd = get_multires_modifier(scene, &object_eval, false); const bool is_sculpt_mode = (object->mode & OB_MODE_SCULPT) != 0; const bool has_multires = mmd != nullptr && mmd->sculptlvl > 0; const ModifierEvalContext mectx = {depsgraph, &object_eval, ModifierApplyFlag(0)}; diff --git a/source/blender/blenkernel/intern/cryptomatte_test.cc b/source/blender/blenkernel/intern/cryptomatte_test.cc index 2f15242b4a4..bb09b276645 100644 --- a/source/blender/blenkernel/intern/cryptomatte_test.cc +++ b/source/blender/blenkernel/intern/cryptomatte_test.cc @@ -163,7 +163,7 @@ TEST(cryptomatte, session_from_stamp_data) * best as possible. */ TEST(cryptomatte, parsing_malformed_manifests) { - /* Manifest from multilayer.exr in the cryptomatte git-repository. */ + /* Manifest from `multilayer.exr` in the cryptomatte git-repository. */ test_cryptomatte_manifest( R"({"/obj/instance1:instances:0":"0d54c6cc","/obj/instance1:instances:1":"293d9340","/obj/instance1:instances:110":"ccb9e1f2","/obj/instance1:instances:111":"f8dd3a48","/obj/instance1:instances:112":"a99e07a8","/obj/instance1:instances:113":"e75599a4","/obj/instance1:instances:114":"794200f3","/obj/instance1:instances:115":"2a3a1728","/obj/instance1:instances:116":"478544a1","/obj/instance1:instances:117":"b2bd969a","/obj/instance1:instances:10":"3a0c8681","/obj/instance1:instances:11":"01e5970d","/obj/box:polygons:1":"9d416418","/obj/instance1:instances:100":"2dcd2966","/obj/instance1:instances:101":"9331cd82","/obj/instance1:instances:102":"df50fccb","/obj/instance1:instances:103":"97f8590d","/obj/instance1:instances:104":"bbcd220d","/obj/instance1:instances:105":"4ae06139","/obj/instance1:instances:106":"8873d5ea","/obj/instance1:instances:107":"39d8af8d","/obj/instance1:instances:108":"bb11bd4e","/obj/instance1:instances:109":"a32bba35"})", R"({"\/obj\/box:polygons:1":"9d416418","\/obj\/instance1:instances:0":"0d54c6cc","\/obj\/instance1:instances:1":"293d9340","\/obj\/instance1:instances:10":"3a0c8681","\/obj\/instance1:instances:100":"2dcd2966","\/obj\/instance1:instances:101":"9331cd82","\/obj\/instance1:instances:102":"df50fccb","\/obj\/instance1:instances:103":"97f8590d","\/obj\/instance1:instances:104":"bbcd220d","\/obj\/instance1:instances:105":"4ae06139","\/obj\/instance1:instances:106":"8873d5ea","\/obj\/instance1:instances:107":"39d8af8d","\/obj\/instance1:instances:108":"bb11bd4e","\/obj\/instance1:instances:109":"a32bba35","\/obj\/instance1:instances:11":"01e5970d","\/obj\/instance1:instances:110":"ccb9e1f2","\/obj\/instance1:instances:111":"f8dd3a48","\/obj\/instance1:instances:112":"a99e07a8","\/obj\/instance1:instances:113":"e75599a4","\/obj\/instance1:instances:114":"794200f3","\/obj\/instance1:instances:115":"2a3a1728","\/obj\/instance1:instances:116":"478544a1","\/obj\/instance1:instances:117":"b2bd969a","\/obj\/instance1:instance)"); diff --git a/source/blender/blenkernel/intern/curve.cc b/source/blender/blenkernel/intern/curve.cc index 5125e010b81..40b64aa8dc8 100644 --- a/source/blender/blenkernel/intern/curve.cc +++ b/source/blender/blenkernel/intern/curve.cc @@ -2470,7 +2470,7 @@ static void make_bevel_list_segment_2D(BevList *bl) static void make_bevel_list_2D(BevList *bl) { - /* NOTE(campbell): `bevp->dir` and `bevp->quat` are not needed for beveling but are + /* NOTE(@campbellbarton): `bevp->dir` and `bevp->quat` are not needed for beveling but are * used when making a path from a 2D curve, therefore they need to be set. */ BevPoint *bevp0, *bevp1, *bevp2; diff --git a/source/blender/blenkernel/intern/curve_nurbs.cc b/source/blender/blenkernel/intern/curve_nurbs.cc index 3ab6fb01ea5..62d5682da0f 100644 --- a/source/blender/blenkernel/intern/curve_nurbs.cc +++ b/source/blender/blenkernel/intern/curve_nurbs.cc @@ -4,8 +4,9 @@ * \ingroup bke */ -#include "BKE_attribute_math.hh" +#include "BLI_task.hh" +#include "BKE_attribute_math.hh" #include "BKE_curves.hh" namespace blender::bke::curves::nurbs { @@ -192,16 +193,16 @@ static void interpolate_to_evaluated(const BasisCache &basis_cache, { attribute_math::DefaultMixer<T> mixer{dst}; - for (const int i : dst.index_range()) { - Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); - - for (const int j : point_weights.index_range()) { - const int point_index = (basis_cache.start_indices[i] + j) % src.size(); - mixer.mix_in(i, src[point_index], point_weights[j]); + threading::parallel_for(dst.index_range(), 128, [&](const IndexRange range) { + for (const int i : range) { + Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); + for (const int j : point_weights.index_range()) { + const int point_index = (basis_cache.start_indices[i] + j) % src.size(); + mixer.mix_in(i, src[point_index], point_weights[j]); + } } - } - - mixer.finalize(); + mixer.finalize(range); + }); } template<typename T> @@ -213,17 +214,18 @@ static void interpolate_to_evaluated_rational(const BasisCache &basis_cache, { attribute_math::DefaultMixer<T> mixer{dst}; - for (const int i : dst.index_range()) { - Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); + threading::parallel_for(dst.index_range(), 128, [&](const IndexRange range) { + for (const int i : range) { + Span<float> point_weights = basis_cache.weights.as_span().slice(i * order, order); - for (const int j : point_weights.index_range()) { - const int point_index = (basis_cache.start_indices[i] + j) % src.size(); - const float weight = point_weights[j] * control_weights[point_index]; - mixer.mix_in(i, src[point_index], weight); + for (const int j : point_weights.index_range()) { + const int point_index = (basis_cache.start_indices[i] + j) % src.size(); + const float weight = point_weights[j] * control_weights[point_index]; + mixer.mix_in(i, src[point_index], weight); + } } - } - - mixer.finalize(); + mixer.finalize(range); + }); } void interpolate_to_evaluated(const BasisCache &basis_cache, diff --git a/source/blender/blenkernel/intern/curves_geometry.cc b/source/blender/blenkernel/intern/curves_geometry.cc index 6486be4afe0..6fdcb56fc91 100644 --- a/source/blender/blenkernel/intern/curves_geometry.cc +++ b/source/blender/blenkernel/intern/curves_geometry.cc @@ -13,6 +13,7 @@ #include "BLI_index_mask_ops.hh" #include "BLI_length_parameterize.hh" #include "BLI_math_rotation.hh" +#include "BLI_task.hh" #include "DNA_curves_types.h" @@ -1163,6 +1164,10 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, } CurvesGeometry new_curves{new_point_count, new_curve_count}; + Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE); threading::parallel_invoke( 256 < new_point_count * new_curve_count, @@ -1170,8 +1175,7 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, [&]() { new_curves.offsets_for_write().copy_from(new_curve_offsets); }, [&]() { /* Copy over point attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) { + for (bke::AttributeTransferData &attribute : point_attributes) { threading::parallel_for(copy_point_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { const IndexRange src_range = copy_point_ranges[range_i]; @@ -1182,24 +1186,29 @@ static CurvesGeometry copy_with_removed_points(const CurvesGeometry &curves, {copy_point_range_dst_offsets[range_i], src_range.size()}); } }); - attribute.dst.finish(); } - + }, + [&]() { /* Copy over curve attributes. - * In some cases points are just dissolved, so the the number of + * In some cases points are just dissolved, so the number of * curves will be the same. That could be optimized in the future. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) { + for (bke::AttributeTransferData &attribute : curve_attributes) { if (new_curves.curves_num() == curves.curves_num()) { attribute.dst.span.copy_from(attribute.src); } else { copy_with_map(attribute.src, new_curve_orig_indices, attribute.dst.span); } - attribute.dst.finish(); } }); + for (bke::AttributeTransferData &attribute : point_attributes) { + attribute.dst.finish(); + } + for (bke::AttributeTransferData &attribute : curve_attributes) { + attribute.dst.finish(); + } + return new_curves; } @@ -1236,6 +1245,10 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, } CurvesGeometry new_curves{new_tot_points, new_tot_curves}; + Vector<bke::AttributeTransferData> point_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT); + Vector<bke::AttributeTransferData> curve_attributes = bke::retrieve_attributes_for_transfer( + curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE); threading::parallel_invoke( 256 < new_tot_points * new_tot_curves, @@ -1267,8 +1280,7 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, }, [&]() { /* Copy over point attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_POINT)) { + for (bke::AttributeTransferData &attribute : point_attributes) { threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { copy_between_buffers(attribute.src.type(), @@ -1278,11 +1290,11 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, new_point_ranges[range_i]); } }); - attribute.dst.finish(); } + }, + [&]() { /* Copy over curve attributes. */ - for (auto &attribute : bke::retrieve_attributes_for_transfer( - curves.attributes(), new_curves.attributes_for_write(), ATTR_DOMAIN_MASK_CURVE)) { + for (bke::AttributeTransferData &attribute : curve_attributes) { threading::parallel_for(old_curve_ranges.index_range(), 128, [&](IndexRange range) { for (const int range_i : range) { copy_between_buffers(attribute.src.type(), @@ -1292,10 +1304,16 @@ static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves, new_curve_ranges[range_i]); } }); - attribute.dst.finish(); } }); + for (bke::AttributeTransferData &attribute : point_attributes) { + attribute.dst.finish(); + } + for (bke::AttributeTransferData &attribute : curve_attributes) { + attribute.dst.finish(); + } + return new_curves; } @@ -1450,12 +1468,15 @@ static void adapt_curve_domain_point_to_curve_impl(const CurvesGeometry &curves, MutableSpan<T> r_values) { attribute_math::DefaultMixer<T> mixer(r_values); - for (const int i_curve : IndexRange(curves.curves_num())) { - for (const int i_point : curves.points_for_curve(i_curve)) { - mixer.mix_in(i_curve, old_values[i_point]); + + threading::parallel_for(curves.curves_range(), 128, [&](const IndexRange range) { + for (const int i_curve : range) { + for (const int i_point : curves.points_for_curve(i_curve)) { + mixer.mix_in(i_curve, old_values[i_point]); + } } - } - mixer.finalize(); + mixer.finalize(range); + }); } /** diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index 82356e06d2c..69825031795 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -26,6 +26,7 @@ #include "BLI_math_vector.hh" #include "BLI_mempool.h" #include "BLI_path_util.h" +#include "BLI_set.hh" #include "BLI_span.hh" #include "BLI_string.h" #include "BLI_string_ref.hh" @@ -58,6 +59,7 @@ #include "data_transfer_intern.h" using blender::IndexRange; +using blender::Set; using blender::Span; using blender::StringRef; using blender::Vector; @@ -165,7 +167,7 @@ struct LayerTypeInfo { void (*initminmax)(void *min, void *max); void (*add)(void *data1, const void *data2); void (*dominmax)(const void *data1, void *min, void *max); - void (*copyvalue)(const void *source, void *dest, const int mixmode, const float mixfactor); + void (*copyvalue)(const void *source, void *dest, int mixmode, const float mixfactor); /** a function to read data from a cdf file */ bool (*read)(CDataFile *cdf, void *data, int count); @@ -187,7 +189,7 @@ struct LayerTypeInfo { /** \name Callbacks for (#MDeformVert, #CD_MDEFORMVERT) * \{ */ -static void layerCopy_mdeformvert(const void *source, void *dest, int count) +static void layerCopy_mdeformvert(const void *source, void *dest, const int count) { int i, size = sizeof(MDeformVert); @@ -209,7 +211,7 @@ static void layerCopy_mdeformvert(const void *source, void *dest, int count) } } -static void layerFree_mdeformvert(void *data, int count, int size) +static void layerFree_mdeformvert(void *data, const int count, const int size) { for (int i = 0; i < count; i++) { MDeformVert *dvert = static_cast<MDeformVert *>(POINTER_OFFSET(data, i * size)); @@ -222,38 +224,10 @@ static void layerFree_mdeformvert(void *data, int count, int size) } } -/* copy just zeros in this case */ -static void layerCopy_bmesh_elem_py_ptr(const void *UNUSED(source), void *dest, int count) -{ - const int size = sizeof(void *); - - for (int i = 0; i < count; i++) { - void **ptr = (void **)POINTER_OFFSET(dest, i * size); - *ptr = nullptr; - } -} - -#ifndef WITH_PYTHON -void bpy_bm_generic_invalidate(struct BPy_BMGeneric *UNUSED(self)) -{ - /* dummy */ -} -#endif - -static void layerFree_bmesh_elem_py_ptr(void *data, int count, int size) -{ - for (int i = 0; i < count; i++) { - void **ptr = (void **)POINTER_OFFSET(data, i * size); - if (*ptr) { - bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(*ptr)); - } - } -} - static void layerInterp_mdeformvert(const void **sources, const float *weights, const float *UNUSED(sub_weights), - int count, + const int count, void *dest) { /* a single linked list of MDeformWeight's @@ -347,7 +321,7 @@ static void layerInterp_mdeformvert(const void **sources, static void layerInterp_normal(const void **sources, const float *weights, const float *UNUSED(sub_weights), - int count, + const int count, void *dest) { /* NOTE: This is linear interpolation, which is not optimal for vectors. @@ -355,8 +329,8 @@ static void layerInterp_normal(const void **sources, * so for now it will do... */ float no[3] = {0.0f}; - while (count--) { - madd_v3_v3fl(no, (const float *)sources[count], weights[count]); + for (const int i : IndexRange(count)) { + madd_v3_v3fl(no, (const float *)sources[i], weights[i]); } /* Weighted sum of normalized vectors will **not** be normalized, even if weights are. */ @@ -406,7 +380,7 @@ static void layerCopyValue_normal(const void *source, /** \name Callbacks for (#MTFace, #CD_MTFACE) * \{ */ -static void layerCopy_tface(const void *source, void *dest, int count) +static void layerCopy_tface(const void *source, void *dest, const int count) { const MTFace *source_tf = (const MTFace *)source; MTFace *dest_tf = (MTFace *)dest; @@ -415,8 +389,11 @@ static void layerCopy_tface(const void *source, void *dest, int count) } } -static void layerInterp_tface( - const void **sources, const float *weights, const float *sub_weights, int count, void *dest) +static void layerInterp_tface(const void **sources, + const float *weights, + const float *sub_weights, + const int count, + void *dest) { MTFace *tf = static_cast<MTFace *>(dest); float uv[4][2] = {{0.0f}}; @@ -456,7 +433,7 @@ static void layerSwap_tface(void *data, const int *corner_indices) memcpy(tf->uv, uv, sizeof(tf->uv)); } -static void layerDefault_tface(void *data, int count) +static void layerDefault_tface(void *data, const int count) { static MTFace default_tf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}}; MTFace *tf = (MTFace *)data; @@ -477,7 +454,7 @@ static int layerMaxNum_tface() /** \name Callbacks for (#MFloatProperty, #CD_PROP_FLOAT) * \{ */ -static void layerCopy_propFloat(const void *source, void *dest, int count) +static void layerCopy_propFloat(const void *source, void *dest, const int count) { memcpy(dest, source, sizeof(MFloatProperty) * count); } @@ -485,7 +462,7 @@ static void layerCopy_propFloat(const void *source, void *dest, int count) static void layerInterp_propFloat(const void **sources, const float *weights, const float *UNUSED(sub_weights), - int count, + const int count, void *dest) { float result = 0.0f; @@ -520,7 +497,7 @@ static bool layerValidate_propFloat(void *data, const uint totitems, const bool /** \name Callbacks for (#MIntProperty, #CD_PROP_INT32) * \{ */ -static void layerCopy_propInt(const void *source, void *dest, int count) +static void layerCopy_propInt(const void *source, void *dest, const int count) { memcpy(dest, source, sizeof(MIntProperty) * count); } @@ -528,7 +505,7 @@ static void layerCopy_propInt(const void *source, void *dest, int count) static void layerInterp_propInt(const void **sources, const float *weights, const float *UNUSED(sub_weights), - int count, + const int count, void *dest) { float result = 0.0f; @@ -547,7 +524,7 @@ static void layerInterp_propInt(const void **sources, /** \name Callbacks for (#MStringProperty, #CD_PROP_STRING) * \{ */ -static void layerCopy_propString(const void *source, void *dest, int count) +static void layerCopy_propString(const void *source, void *dest, const int count) { memcpy(dest, source, sizeof(MStringProperty) * count); } @@ -558,7 +535,7 @@ static void layerCopy_propString(const void *source, void *dest, int count) /** \name Callbacks for (#OrigSpaceFace, #CD_ORIGSPACE) * \{ */ -static void layerCopy_origspace_face(const void *source, void *dest, int count) +static void layerCopy_origspace_face(const void *source, void *dest, const int count) { const OrigSpaceFace *source_tf = (const OrigSpaceFace *)source; OrigSpaceFace *dest_tf = (OrigSpaceFace *)dest; @@ -568,8 +545,11 @@ static void layerCopy_origspace_face(const void *source, void *dest, int count) } } -static void layerInterp_origspace_face( - const void **sources, const float *weights, const float *sub_weights, int count, void *dest) +static void layerInterp_origspace_face(const void **sources, + const float *weights, + const float *sub_weights, + const int count, + void *dest) { OrigSpaceFace *osf = static_cast<OrigSpaceFace *>(dest); float uv[4][2] = {{0.0f}}; @@ -606,7 +586,7 @@ static void layerSwap_origspace_face(void *data, const int *corner_indices) memcpy(osf->uv, uv, sizeof(osf->uv)); } -static void layerDefault_origspace_face(void *data, int count) +static void layerDefault_origspace_face(void *data, const int count) { static OrigSpaceFace default_osf = {{{0, 0}, {1, 0}, {1, 1}, {0, 1}}}; OrigSpaceFace *osf = (OrigSpaceFace *)data; @@ -652,7 +632,7 @@ static void layerSwap_mdisps(void *data, const int *ci) } } -static void layerCopy_mdisps(const void *source, void *dest, int count) +static void layerCopy_mdisps(const void *source, void *dest, const int count) { const MDisps *s = static_cast<const MDisps *>(source); MDisps *d = static_cast<MDisps *>(dest); @@ -673,7 +653,7 @@ static void layerCopy_mdisps(const void *source, void *dest, int count) } } -static void layerFree_mdisps(void *data, int count, int UNUSED(size)) +static void layerFree_mdisps(void *data, const int count, const int UNUSED(size)) { MDisps *d = static_cast<MDisps *>(data); @@ -691,7 +671,7 @@ static void layerFree_mdisps(void *data, int count, int UNUSED(size)) } } -static bool layerRead_mdisps(CDataFile *cdf, void *data, int count) +static bool layerRead_mdisps(CDataFile *cdf, void *data, const int count) { MDisps *d = static_cast<MDisps *>(data); @@ -709,7 +689,7 @@ static bool layerRead_mdisps(CDataFile *cdf, void *data, int count) return true; } -static bool layerWrite_mdisps(CDataFile *cdf, const void *data, int count) +static bool layerWrite_mdisps(CDataFile *cdf, const void *data, const int count) { const MDisps *d = static_cast<const MDisps *>(data); @@ -723,7 +703,7 @@ static bool layerWrite_mdisps(CDataFile *cdf, const void *data, int count) return true; } -static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, int count) +static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, const int count) { const MDisps *d = static_cast<const MDisps *>(data); size_t size = 0; @@ -738,6 +718,40 @@ static size_t layerFilesize_mdisps(CDataFile *UNUSED(cdf), const void *data, int /** \} */ /* -------------------------------------------------------------------- */ +/** \name Callbacks for (#CD_BM_ELEM_PYPTR) + * \{ */ + +/* copy just zeros in this case */ +static void layerCopy_bmesh_elem_py_ptr(const void *UNUSED(source), void *dest, const int count) +{ + const int size = sizeof(void *); + + for (int i = 0; i < count; i++) { + void **ptr = (void **)POINTER_OFFSET(dest, i * size); + *ptr = nullptr; + } +} + +#ifndef WITH_PYTHON +void bpy_bm_generic_invalidate(struct BPy_BMGeneric *UNUSED(self)) +{ + /* dummy */ +} +#endif + +static void layerFree_bmesh_elem_py_ptr(void *data, const int count, const int size) +{ + for (int i = 0; i < count; i++) { + void **ptr = (void **)POINTER_OFFSET(data, i * size); + if (*ptr) { + bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(*ptr)); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Callbacks for (`float`, #CD_PAINT_MASK) * \{ */ @@ -762,7 +776,7 @@ static void layerInterp_paint_mask(const void **sources, /** \name Callbacks for (#GridPaintMask, #CD_GRID_PAINT_MASK) * \{ */ -static void layerCopy_grid_paint_mask(const void *source, void *dest, int count) +static void layerCopy_grid_paint_mask(const void *source, void *dest, const int count) { const GridPaintMask *s = static_cast<const GridPaintMask *>(source); GridPaintMask *d = static_cast<GridPaintMask *>(dest); @@ -779,7 +793,7 @@ static void layerCopy_grid_paint_mask(const void *source, void *dest, int count) } } -static void layerFree_grid_paint_mask(void *data, int count, int UNUSED(size)) +static void layerFree_grid_paint_mask(void *data, const int count, const int UNUSED(size)) { GridPaintMask *gpm = static_cast<GridPaintMask *>(data); @@ -867,7 +881,7 @@ static bool layerEqual_mloopcol(const void *data1, const void *data2) return r * r + g * g + b * b + a * a < 0.001f; } -static void layerMultiply_mloopcol(void *data, float fac) +static void layerMultiply_mloopcol(void *data, const float fac) { MLoopCol *m = static_cast<MLoopCol *>(data); @@ -936,7 +950,7 @@ static void layerInitMinMax_mloopcol(void *vmin, void *vmax) max->a = 0; } -static void layerDefault_mloopcol(void *data, int count) +static void layerDefault_mloopcol(void *data, const int count) { MLoopCol default_mloopcol = {255, 255, 255, 255}; MLoopCol *mlcol = (MLoopCol *)data; @@ -1016,7 +1030,7 @@ static bool layerEqual_mloopuv(const void *data1, const void *data2) return len_squared_v2v2(luv1->uv, luv2->uv) < 0.00001f; } -static void layerMultiply_mloopuv(void *data, float fac) +static void layerMultiply_mloopuv(void *data, const float fac) { MLoopUV *luv = static_cast<MLoopUV *>(data); @@ -1110,7 +1124,7 @@ static bool layerEqual_mloop_origspace(const void *data1, const void *data2) return len_squared_v2v2(luv1->uv, luv2->uv) < 0.00001f; } -static void layerMultiply_mloop_origspace(void *data, float fac) +static void layerMultiply_mloop_origspace(void *data, const float fac) { OrigSpaceLoop *luv = static_cast<OrigSpaceLoop *>(data); @@ -1162,8 +1176,11 @@ static void layerInterp_mloop_origspace(const void **sources, } /* --- end copy */ -static void layerInterp_mcol( - const void **sources, const float *weights, const float *sub_weights, int count, void *dest) +static void layerInterp_mcol(const void **sources, + const float *weights, + const float *sub_weights, + const int count, + void *dest) { MCol *mc = static_cast<MCol *>(dest); struct { @@ -1222,7 +1239,7 @@ static void layerSwap_mcol(void *data, const int *corner_indices) memcpy(mcol, col, sizeof(col)); } -static void layerDefault_mcol(void *data, int count) +static void layerDefault_mcol(void *data, const int count) { static MCol default_mcol = {255, 255, 255, 255}; MCol *mcol = (MCol *)data; @@ -1232,7 +1249,7 @@ static void layerDefault_mcol(void *data, int count) } } -static void layerDefault_origindex(void *data, int count) +static void layerDefault_origindex(void *data, const int count) { copy_vn_i((int *)data, count, ORIGINDEX_NONE); } @@ -1290,7 +1307,7 @@ static void layerInterp_shapekey(const void **sources, /** \name Callbacks for (#MVertSkin, #CD_MVERT_SKIN) * \{ */ -static void layerDefault_mvert_skin(void *data, int count) +static void layerDefault_mvert_skin(void *data, const int count) { MVertSkin *vs = static_cast<MVertSkin *>(data); @@ -1300,7 +1317,7 @@ static void layerDefault_mvert_skin(void *data, int count) } } -static void layerCopy_mvert_skin(const void *source, void *dest, int count) +static void layerCopy_mvert_skin(const void *source, void *dest, const int count) { memcpy(dest, source, sizeof(MVertSkin) * count); } @@ -1352,7 +1369,7 @@ static void layerSwap_flnor(void *data, const int *corner_indices) /** \name Callbacks for (`int`, #CD_FACEMAP) * \{ */ -static void layerDefault_fmap(void *data, int count) +static void layerDefault_fmap(void *data, const int count) { int *fmap_num = (int *)data; for (int i = 0; i < count; i++) { @@ -1428,7 +1445,7 @@ static bool layerEqual_propcol(const void *data1, const void *data2) return tot < 0.001f; } -static void layerMultiply_propcol(void *data, float fac) +static void layerMultiply_propcol(void *data, const float fac) { MPropCol *m = static_cast<MPropCol *>(data); mul_v4_fl(m->color, fac); @@ -1458,7 +1475,7 @@ static void layerInitMinMax_propcol(void *vmin, void *vmax) copy_v4_fl(max->color, FLT_MIN); } -static void layerDefault_propcol(void *data, int count) +static void layerDefault_propcol(void *data, const int count) { /* Default to white, full alpha. */ MPropCol default_propcol = {{1.0f, 1.0f, 1.0f, 1.0f}}; @@ -1510,7 +1527,7 @@ static void layerInterp_propfloat3(const void **sources, copy_v3_v3((float *)dest, &result.x); } -static void layerMultiply_propfloat3(void *data, float fac) +static void layerMultiply_propfloat3(void *data, const float fac) { vec3f *vec = static_cast<vec3f *>(data); vec->x *= fac; @@ -1563,7 +1580,7 @@ static void layerInterp_propfloat2(const void **sources, copy_v2_v2((float *)dest, &result.x); } -static void layerMultiply_propfloat2(void *data, float fac) +static void layerMultiply_propfloat2(void *data, const float fac) { vec2f *vec = static_cast<vec2f *>(data); vec->x *= fac; @@ -2327,7 +2344,44 @@ bool CustomData_merge(const CustomData *source, return changed; } -void CustomData_realloc(CustomData *data, int totelem) +static bool attribute_stored_in_bmesh_flag(const StringRef name) +{ + return ELEM(name, ".hide_vert", ".hide_edge", ".hide_poly"); +} + +static CustomData shallow_copy_remove_non_bmesh_attributes(const CustomData &src) +{ + Vector<CustomDataLayer> dst_layers; + for (const CustomDataLayer &layer : Span<CustomDataLayer>{src.layers, src.totlayer}) { + if (!attribute_stored_in_bmesh_flag(layer.name)) { + dst_layers.append(layer); + } + } + + CustomData dst = src; + dst.layers = static_cast<CustomDataLayer *>( + MEM_calloc_arrayN(dst_layers.size(), sizeof(CustomDataLayer), __func__)); + dst.totlayer = dst_layers.size(); + memcpy(dst.layers, dst_layers.data(), dst_layers.as_span().size_in_bytes()); + + CustomData_update_typemap(&dst); + + return dst; +} + +bool CustomData_merge_mesh_to_bmesh(const CustomData *source, + CustomData *dest, + const eCustomDataMask mask, + const eCDAllocType alloctype, + const int totelem) +{ + CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source); + const bool result = CustomData_merge(&source_copy, dest, mask, alloctype, totelem); + MEM_SAFE_FREE(source_copy.layers); + return result; +} + +void CustomData_realloc(CustomData *data, const int totelem) { BLI_assert(totelem >= 0); for (int i = 0; i < data->totlayer; i++) { @@ -2358,7 +2412,18 @@ void CustomData_copy(const CustomData *source, CustomData_merge(source, dest, mask, alloctype, totelem); } -static void customData_free_layer__internal(CustomDataLayer *layer, int totelem) +void CustomData_copy_mesh_to_bmesh(const CustomData *source, + CustomData *dest, + const eCustomDataMask mask, + const eCDAllocType alloctype, + const int totelem) +{ + CustomData source_copy = shallow_copy_remove_non_bmesh_attributes(*source); + CustomData_copy(&source_copy, dest, mask, alloctype, totelem); + MEM_SAFE_FREE(source_copy.layers); +} + +static void customData_free_layer__internal(CustomDataLayer *layer, const int totelem) { const LayerTypeInfo *typeInfo; @@ -2393,7 +2458,7 @@ void CustomData_reset(CustomData *data) copy_vn_i(data->typemap, CD_NUMTYPES, -1); } -void CustomData_free(CustomData *data, int totelem) +void CustomData_free(CustomData *data, const int totelem) { for (int i = 0; i < data->totlayer; i++) { customData_free_layer__internal(&data->layers[i], totelem); @@ -2407,7 +2472,7 @@ void CustomData_free(CustomData *data, int totelem) CustomData_reset(data); } -void CustomData_free_typemask(CustomData *data, int totelem, eCustomDataMask mask) +void CustomData_free_typemask(CustomData *data, const int totelem, eCustomDataMask mask) { for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; @@ -2442,7 +2507,7 @@ static void customData_update_offsets(CustomData *data) } /* to use when we're in the middle of modifying layers */ -static int CustomData_get_layer_index__notypemap(const CustomData *data, int type) +static int CustomData_get_layer_index__notypemap(const CustomData *data, const int type) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2456,13 +2521,13 @@ static int CustomData_get_layer_index__notypemap(const CustomData *data, int typ /* -------------------------------------------------------------------- */ /* index values to access the layers (offset from the layer start) */ -int CustomData_get_layer_index(const CustomData *data, int type) +int CustomData_get_layer_index(const CustomData *data, const int type) { BLI_assert(customdata_typemap_is_valid(data)); return data->typemap[type]; } -int CustomData_get_layer_index_n(const CustomData *data, int type, int n) +int CustomData_get_layer_index_n(const CustomData *data, const int type, const int n) { BLI_assert(n >= 0); int i = CustomData_get_layer_index(data, type); @@ -2475,7 +2540,7 @@ int CustomData_get_layer_index_n(const CustomData *data, int type, int n) return i; } -int CustomData_get_named_layer_index(const CustomData *data, int type, const char *name) +int CustomData_get_named_layer_index(const CustomData *data, const int type, const char *name) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2488,28 +2553,28 @@ int CustomData_get_named_layer_index(const CustomData *data, int type, const cha return -1; } -int CustomData_get_active_layer_index(const CustomData *data, int type) +int CustomData_get_active_layer_index(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? layer_index + data->layers[layer_index].active : -1; } -int CustomData_get_render_layer_index(const CustomData *data, int type) +int CustomData_get_render_layer_index(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? layer_index + data->layers[layer_index].active_rnd : -1; } -int CustomData_get_clone_layer_index(const CustomData *data, int type) +int CustomData_get_clone_layer_index(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? layer_index + data->layers[layer_index].active_clone : -1; } -int CustomData_get_stencil_layer_index(const CustomData *data, int type) +int CustomData_get_stencil_layer_index(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2519,7 +2584,7 @@ int CustomData_get_stencil_layer_index(const CustomData *data, int type) /* -------------------------------------------------------------------- */ /* index values per layer type */ -int CustomData_get_named_layer(const CustomData *data, int type, const char *name) +int CustomData_get_named_layer(const CustomData *data, const int type, const char *name) { const int named_index = CustomData_get_named_layer_index(data, type, name); const int layer_index = data->typemap[type]; @@ -2527,28 +2592,28 @@ int CustomData_get_named_layer(const CustomData *data, int type, const char *nam return (named_index != -1) ? named_index - layer_index : -1; } -int CustomData_get_active_layer(const CustomData *data, int type) +int CustomData_get_active_layer(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? data->layers[layer_index].active : -1; } -int CustomData_get_render_layer(const CustomData *data, int type) +int CustomData_get_render_layer(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? data->layers[layer_index].active_rnd : -1; } -int CustomData_get_clone_layer(const CustomData *data, int type) +int CustomData_get_clone_layer(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); return (layer_index != -1) ? data->layers[layer_index].active_clone : -1; } -int CustomData_get_stencil_layer(const CustomData *data, int type) +int CustomData_get_stencil_layer(const CustomData *data, const int type) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2562,7 +2627,7 @@ const char *CustomData_get_active_layer_name(const CustomData *data, const int t return layer_index < 0 ? nullptr : data->layers[layer_index].name; } -void CustomData_set_layer_active(CustomData *data, int type, int n) +void CustomData_set_layer_active(CustomData *data, const int type, const int n) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2571,7 +2636,7 @@ void CustomData_set_layer_active(CustomData *data, int type, int n) } } -void CustomData_set_layer_render(CustomData *data, int type, int n) +void CustomData_set_layer_render(CustomData *data, const int type, const int n) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2580,7 +2645,7 @@ void CustomData_set_layer_render(CustomData *data, int type, int n) } } -void CustomData_set_layer_clone(CustomData *data, int type, int n) +void CustomData_set_layer_clone(CustomData *data, const int type, const int n) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2589,7 +2654,7 @@ void CustomData_set_layer_clone(CustomData *data, int type, int n) } } -void CustomData_set_layer_stencil(CustomData *data, int type, int n) +void CustomData_set_layer_stencil(CustomData *data, const int type, const int n) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2598,7 +2663,7 @@ void CustomData_set_layer_stencil(CustomData *data, int type, int n) } } -void CustomData_set_layer_active_index(CustomData *data, int type, int n) +void CustomData_set_layer_active_index(CustomData *data, const int type, const int n) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2610,7 +2675,7 @@ void CustomData_set_layer_active_index(CustomData *data, int type, int n) } } -void CustomData_set_layer_render_index(CustomData *data, int type, int n) +void CustomData_set_layer_render_index(CustomData *data, const int type, const int n) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2622,7 +2687,7 @@ void CustomData_set_layer_render_index(CustomData *data, int type, int n) } } -void CustomData_set_layer_clone_index(CustomData *data, int type, int n) +void CustomData_set_layer_clone_index(CustomData *data, const int type, const int n) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2634,7 +2699,7 @@ void CustomData_set_layer_clone_index(CustomData *data, int type, int n) } } -void CustomData_set_layer_stencil_index(CustomData *data, int type, int n) +void CustomData_set_layer_stencil_index(CustomData *data, const int type, const int n) { const int layer_index = data->typemap[type]; BLI_assert(customdata_typemap_is_valid(data)); @@ -2646,7 +2711,7 @@ void CustomData_set_layer_stencil_index(CustomData *data, int type, int n) } } -void CustomData_set_layer_flag(CustomData *data, int type, int flag) +void CustomData_set_layer_flag(CustomData *data, const int type, const int flag) { for (int i = 0; i < data->totlayer; i++) { if (data->layers[i].type == type) { @@ -2655,7 +2720,7 @@ void CustomData_set_layer_flag(CustomData *data, int type, int flag) } } -void CustomData_clear_layer_flag(CustomData *data, int type, int flag) +void CustomData_clear_layer_flag(CustomData *data, const int type, const int flag) { const int nflag = ~flag; @@ -2666,7 +2731,7 @@ void CustomData_clear_layer_flag(CustomData *data, int type, int flag) } } -static bool customData_resize(CustomData *data, int amount) +static bool customData_resize(CustomData *data, const int amount) { CustomDataLayer *tmp = static_cast<CustomDataLayer *>( MEM_calloc_arrayN((data->maxlayer + amount), sizeof(*tmp), __func__)); @@ -2685,15 +2750,14 @@ static bool customData_resize(CustomData *data, int amount) } static CustomDataLayer *customData_add_layer__internal(CustomData *data, - int type, - eCDAllocType alloctype, + const int type, + const eCDAllocType alloctype, void *layerdata, - int totelem, + const int totelem, const char *name) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); - int flag = 0, index = data->totlayer; - void *newlayerdata = nullptr; + int flag = 0; /* Passing a layer-data to copy from with an alloctype that won't copy is * most likely a bug */ @@ -2703,6 +2767,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, return &data->layers[CustomData_get_layer_index(data, type)]; } + void *newlayerdata = nullptr; if (ELEM(alloctype, CD_ASSIGN, CD_REFERENCE)) { newlayerdata = layerdata; } @@ -2738,6 +2803,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, flag |= CD_FLAG_NOFREE; } + int index = data->totlayer; if (index >= data->maxlayer) { if (!customData_resize(data, CUSTOMDATA_GROW)) { if (newlayerdata != layerdata) { @@ -2754,14 +2820,16 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, data->layers[index] = data->layers[index - 1]; } + CustomDataLayer &new_layer = data->layers[index]; + /* Clear remaining data on the layer. The original data on the layer has been moved to another * index. Without this, it can happen that information from the previous layer at that index * leaks into the new layer. */ - memset(data->layers + index, 0, sizeof(CustomDataLayer)); + memset(&new_layer, 0, sizeof(CustomDataLayer)); - data->layers[index].type = type; - data->layers[index].flag = flag; - data->layers[index].data = newlayerdata; + new_layer.type = type; + new_layer.flag = flag; + new_layer.data = newlayerdata; /* Set default name if none exists. Note we only call DATA_() once * we know there is a default name, to avoid overhead of locale lookups @@ -2771,24 +2839,24 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, } if (name) { - BLI_strncpy(data->layers[index].name, name, sizeof(data->layers[index].name)); + BLI_strncpy(new_layer.name, name, sizeof(new_layer.name)); CustomData_set_layer_unique_name(data, index); } else { - data->layers[index].name[0] = '\0'; + new_layer.name[0] = '\0'; } if (index > 0 && data->layers[index - 1].type == type) { - data->layers[index].active = data->layers[index - 1].active; - data->layers[index].active_rnd = data->layers[index - 1].active_rnd; - data->layers[index].active_clone = data->layers[index - 1].active_clone; - data->layers[index].active_mask = data->layers[index - 1].active_mask; + new_layer.active = data->layers[index - 1].active; + new_layer.active_rnd = data->layers[index - 1].active_rnd; + new_layer.active_clone = data->layers[index - 1].active_clone; + new_layer.active_mask = data->layers[index - 1].active_mask; } else { - data->layers[index].active = 0; - data->layers[index].active_rnd = 0; - data->layers[index].active_clone = 0; - data->layers[index].active_mask = 0; + new_layer.active = 0; + new_layer.active_rnd = 0; + new_layer.active_clone = 0; + new_layer.active_mask = 0; } customData_update_offsets(data); @@ -2797,7 +2865,7 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, } void *CustomData_add_layer( - CustomData *data, int type, eCDAllocType alloctype, void *layerdata, int totelem) + CustomData *data, const int type, eCDAllocType alloctype, void *layerdata, const int totelem) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -2813,10 +2881,10 @@ void *CustomData_add_layer( } void *CustomData_add_layer_named(CustomData *data, - int type, - eCDAllocType alloctype, + const int type, + const eCDAllocType alloctype, void *layerdata, - int totelem, + const int totelem, const char *name) { CustomDataLayer *layer = customData_add_layer__internal( @@ -2831,10 +2899,10 @@ void *CustomData_add_layer_named(CustomData *data, } void *CustomData_add_layer_anonymous(CustomData *data, - int type, - eCDAllocType alloctype, + const int type, + const eCDAllocType alloctype, void *layerdata, - int totelem, + const int totelem, const AnonymousAttributeID *anonymous_id) { const char *name = BKE_anonymous_attribute_id_internal_name(anonymous_id); @@ -2851,7 +2919,7 @@ void *CustomData_add_layer_anonymous(CustomData *data, return layer->data; } -bool CustomData_free_layer(CustomData *data, int type, int totelem, int index) +bool CustomData_free_layer(CustomData *data, const int type, const int totelem, const int index) { const int index_first = CustomData_get_layer_index(data, type); const int n = index - index_first; @@ -2915,7 +2983,7 @@ bool CustomData_free_layer_named(CustomData *data, const char *name, const int t return false; } -bool CustomData_free_layer_active(CustomData *data, int type, int totelem) +bool CustomData_free_layer_active(CustomData *data, const int type, const int totelem) { const int index = CustomData_get_active_layer_index(data, type); if (index == -1) { @@ -2924,7 +2992,7 @@ bool CustomData_free_layer_active(CustomData *data, int type, int totelem) return CustomData_free_layer(data, type, totelem, index); } -void CustomData_free_layers(CustomData *data, int type, int totelem) +void CustomData_free_layers(CustomData *data, const int type, const int totelem) { const int index = CustomData_get_layer_index(data, type); while (CustomData_free_layer(data, type, totelem, index)) { @@ -2932,12 +3000,12 @@ void CustomData_free_layers(CustomData *data, int type, int totelem) } } -bool CustomData_has_layer(const CustomData *data, int type) +bool CustomData_has_layer(const CustomData *data, const int type) { return (CustomData_get_layer_index(data, type) != -1); } -int CustomData_number_of_layers(const CustomData *data, int type) +int CustomData_number_of_layers(const CustomData *data, const int type) { int number = 0; @@ -2950,7 +3018,7 @@ int CustomData_number_of_layers(const CustomData *data, int type) return number; } -int CustomData_number_of_layers_typemask(const CustomData *data, eCustomDataMask mask) +int CustomData_number_of_layers_typemask(const CustomData *data, const eCustomDataMask mask) { int number = 0; @@ -3040,7 +3108,7 @@ void *CustomData_duplicate_referenced_layer_anonymous(CustomData *data, return nullptr; } -void CustomData_duplicate_referenced_layers(CustomData *data, int totelem) +void CustomData_duplicate_referenced_layers(CustomData *data, const int totelem) { for (int i = 0; i < data->totlayer; i++) { CustomDataLayer *layer = &data->layers[i]; @@ -3048,7 +3116,7 @@ void CustomData_duplicate_referenced_layers(CustomData *data, int totelem) } } -bool CustomData_is_referenced_layer(CustomData *data, int type) +bool CustomData_is_referenced_layer(CustomData *data, const int type) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3061,7 +3129,7 @@ bool CustomData_is_referenced_layer(CustomData *data, int type) return (layer->flag & CD_FLAG_NOFREE) != 0; } -void CustomData_free_temporary(CustomData *data, int totelem) +void CustomData_free_temporary(CustomData *data, const int totelem) { int i, j; bool changed = false; @@ -3093,7 +3161,7 @@ void CustomData_free_temporary(CustomData *data, int totelem) } } -void CustomData_set_only_copy(const CustomData *data, eCustomDataMask mask) +void CustomData_set_only_copy(const CustomData *data, const eCustomDataMask mask) { for (int i = 0; i < data->totlayer; i++) { if (!(mask & CD_TYPE_AS_MASK(data->layers[i].type))) { @@ -3102,7 +3170,10 @@ void CustomData_set_only_copy(const CustomData *data, eCustomDataMask mask) } } -void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs, int count) +void CustomData_copy_elements(const int type, + void *src_data_ofs, + void *dst_data_ofs, + const int count) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -3116,11 +3187,11 @@ void CustomData_copy_elements(int type, void *src_data_ofs, void *dst_data_ofs, void CustomData_copy_data_layer(const CustomData *source, CustomData *dest, - int src_layer_index, - int dst_layer_index, - int src_index, - int dst_index, - int count) + const int src_layer_index, + const int dst_layer_index, + const int src_index, + const int dst_index, + const int count) { const LayerTypeInfo *typeInfo; @@ -3154,8 +3225,11 @@ void CustomData_copy_data_layer(const CustomData *source, } } -void CustomData_copy_data_named( - const CustomData *source, CustomData *dest, int source_index, int dest_index, int count) +void CustomData_copy_data_named(const CustomData *source, + CustomData *dest, + const int source_index, + const int dest_index, + const int count) { /* copies a layer at a time */ for (int src_i = 0; src_i < source->totlayer; src_i++) { @@ -3170,8 +3244,11 @@ void CustomData_copy_data_named( } } -void CustomData_copy_data( - const CustomData *source, CustomData *dest, int source_index, int dest_index, int count) +void CustomData_copy_data(const CustomData *source, + CustomData *dest, + const int source_index, + const int dest_index, + const int count) { /* copies a layer at a time */ int dest_i = 0; @@ -3226,7 +3303,7 @@ void CustomData_copy_layer_type_data(const CustomData *source, count); } -void CustomData_free_elem(CustomData *data, int index, int count) +void CustomData_free_elem(CustomData *data, const int index, const int count) { for (int i = 0; i < data->totlayer; i++) { if (!(data->layers[i].flag & CD_FLAG_NOFREE)) { @@ -3326,7 +3403,7 @@ void CustomData_interp(const CustomData *source, } } -void CustomData_swap_corners(CustomData *data, int index, const int *corner_indices) +void CustomData_swap_corners(CustomData *data, const int index, const int *corner_indices) { for (int i = 0; i < data->totlayer; i++) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[i].type); @@ -3366,7 +3443,7 @@ void CustomData_swap(CustomData *data, const int index_a, const int index_b) } } -void *CustomData_get(const CustomData *data, int index, int type) +void *CustomData_get(const CustomData *data, const int index, const int type) { BLI_assert(index >= 0); @@ -3382,7 +3459,7 @@ void *CustomData_get(const CustomData *data, int index, int type) return POINTER_OFFSET(data->layers[layer_index].data, offset); } -void *CustomData_get_n(const CustomData *data, int type, int index, int n) +void *CustomData_get_n(const CustomData *data, const int type, const int index, const int n) { BLI_assert(index >= 0 && n >= 0); @@ -3396,7 +3473,7 @@ void *CustomData_get_n(const CustomData *data, int type, int index, int n) return POINTER_OFFSET(data->layers[layer_index + n].data, offset); } -void *CustomData_get_layer(const CustomData *data, int type) +void *CustomData_get_layer(const CustomData *data, const int type) { /* get the layer index of the active layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3407,7 +3484,7 @@ void *CustomData_get_layer(const CustomData *data, int type) return data->layers[layer_index].data; } -void *CustomData_get_layer_n(const CustomData *data, int type, int n) +void *CustomData_get_layer_n(const CustomData *data, const int type, const int n) { /* get the layer index of the active layer of type */ int layer_index = CustomData_get_layer_index_n(data, type, n); @@ -3418,7 +3495,7 @@ void *CustomData_get_layer_n(const CustomData *data, int type, int n) return data->layers[layer_index].data; } -void *CustomData_get_layer_named(const CustomData *data, int type, const char *name) +void *CustomData_get_layer_named(const CustomData *data, const int type, const char *name) { int layer_index = CustomData_get_named_layer_index(data, type, name); if (layer_index == -1) { @@ -3428,7 +3505,7 @@ void *CustomData_get_layer_named(const CustomData *data, int type, const char *n return data->layers[layer_index].data; } -int CustomData_get_offset(const CustomData *data, int type) +int CustomData_get_offset(const CustomData *data, const int type) { /* get the layer index of the active layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3439,10 +3516,10 @@ int CustomData_get_offset(const CustomData *data, int type) return data->layers[layer_index].offset; } -int CustomData_get_offset_named(const CustomData *data, int type, const char *name) +int CustomData_get_n_offset(const CustomData *data, const int type, const int n) { /* get the layer index of the active layer of type */ - int layer_index = CustomData_get_named_layer_index(data, type, name); + int layer_index = CustomData_get_layer_index_n(data, type, n); if (layer_index == -1) { return -1; } @@ -3450,10 +3527,9 @@ int CustomData_get_offset_named(const CustomData *data, int type, const char *na return data->layers[layer_index].offset; } -int CustomData_get_n_offset(const CustomData *data, int type, int n) +int CustomData_get_offset_named(const CustomData *data, int type, const char *name) { - /* get the layer index of the active layer of type */ - int layer_index = CustomData_get_layer_index_n(data, type, n); + int layer_index = CustomData_get_named_layer_index(data, type, name); if (layer_index == -1) { return -1; } @@ -3461,7 +3537,10 @@ int CustomData_get_n_offset(const CustomData *data, int type, int n) return data->layers[layer_index].offset; } -bool CustomData_set_layer_name(const CustomData *data, int type, int n, const char *name) +bool CustomData_set_layer_name(const CustomData *data, + const int type, + const int n, + const char *name) { /* get the layer index of the first layer of type */ const int layer_index = CustomData_get_layer_index_n(data, type, n); @@ -3475,14 +3554,14 @@ bool CustomData_set_layer_name(const CustomData *data, int type, int n, const ch return true; } -const char *CustomData_get_layer_name(const CustomData *data, int type, int n) +const char *CustomData_get_layer_name(const CustomData *data, const int type, const int n) { const int layer_index = CustomData_get_layer_index_n(data, type, n); return (layer_index == -1) ? nullptr : data->layers[layer_index].name; } -void *CustomData_set_layer(const CustomData *data, int type, void *ptr) +void *CustomData_set_layer(const CustomData *data, const int type, void *ptr) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3496,7 +3575,7 @@ void *CustomData_set_layer(const CustomData *data, int type, void *ptr) return ptr; } -void *CustomData_set_layer_n(const CustomData *data, int type, int n, void *ptr) +void *CustomData_set_layer_n(const CustomData *data, const int type, const int n, void *ptr) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_layer_index_n(data, type, n); @@ -3509,7 +3588,7 @@ void *CustomData_set_layer_n(const CustomData *data, int type, int n, void *ptr) return ptr; } -void CustomData_set(const CustomData *data, int index, int type, const void *source) +void CustomData_set(const CustomData *data, const int index, const int type, const void *source) { void *dest = CustomData_get(data, index, type); const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -3561,7 +3640,7 @@ void CustomData_bmesh_update_active_layers(CustomData *fdata, CustomData *ldata) } } -void CustomData_bmesh_init_pool(CustomData *data, int totelem, const char htype) +void CustomData_bmesh_init_pool(CustomData *data, const int totelem, const char htype) { int chunksize; @@ -3763,7 +3842,7 @@ void CustomData_bmesh_free_block_data_exclude_by_type(CustomData *data, } } -static void CustomData_bmesh_set_default_n(CustomData *data, void **block, int n) +static void CustomData_bmesh_set_default_n(CustomData *data, void **block, const int n) { int offset = data->layers[n].offset; const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type); @@ -3858,7 +3937,7 @@ void CustomData_bmesh_copy_data(const CustomData *source, CustomData_bmesh_copy_data_exclude_by_type(source, dest, src_block, dest_block, 0); } -void *CustomData_bmesh_get(const CustomData *data, void *block, int type) +void *CustomData_bmesh_get(const CustomData *data, void *block, const int type) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_active_layer_index(data, type); @@ -3869,7 +3948,7 @@ void *CustomData_bmesh_get(const CustomData *data, void *block, int type) return POINTER_OFFSET(block, data->layers[layer_index].offset); } -void *CustomData_bmesh_get_n(const CustomData *data, void *block, int type, int n) +void *CustomData_bmesh_get_n(const CustomData *data, void *block, const int type, const int n) { /* get the layer index of the first layer of type */ int layer_index = CustomData_get_layer_index(data, type); @@ -3880,7 +3959,7 @@ void *CustomData_bmesh_get_n(const CustomData *data, void *block, int type, int return POINTER_OFFSET(block, data->layers[layer_index + n].offset); } -void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n) +void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, const int n) { if (n < 0 || n >= data->totlayer) { return nullptr; @@ -3889,7 +3968,7 @@ void *CustomData_bmesh_get_layer_n(const CustomData *data, void *block, int n) return POINTER_OFFSET(block, data->layers[n].offset); } -bool CustomData_layer_has_math(const CustomData *data, int layer_n) +bool CustomData_layer_has_math(const CustomData *data, const int layer_n) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type); @@ -3901,7 +3980,7 @@ bool CustomData_layer_has_math(const CustomData *data, int layer_n) return false; } -bool CustomData_layer_has_interp(const CustomData *data, int layer_n) +bool CustomData_layer_has_interp(const CustomData *data, const int layer_n) { const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[layer_n].type); @@ -4022,7 +4101,7 @@ void CustomData_data_dominmax(int type, const void *data, void *min, void *max) } } -void CustomData_data_multiply(int type, void *data, float fac) +void CustomData_data_multiply(int type, void *data, const float fac) { const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -4040,7 +4119,7 @@ void CustomData_data_add(int type, void *data1, const void *data2) } } -void CustomData_bmesh_set(const CustomData *data, void *block, int type, const void *source) +void CustomData_bmesh_set(const CustomData *data, void *block, const int type, const void *source) { void *dest = CustomData_bmesh_get(data, block, type); const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -4057,7 +4136,8 @@ void CustomData_bmesh_set(const CustomData *data, void *block, int type, const v } } -void CustomData_bmesh_set_n(CustomData *data, void *block, int type, int n, const void *source) +void CustomData_bmesh_set_n( + CustomData *data, void *block, const int type, const int n, const void *source) { void *dest = CustomData_bmesh_get_n(data, block, type, n); const LayerTypeInfo *typeInfo = layerType_getInfo(type); @@ -4074,7 +4154,7 @@ void CustomData_bmesh_set_n(CustomData *data, void *block, int type, int n, cons } } -void CustomData_bmesh_set_layer_n(CustomData *data, void *block, int n, const void *source) +void CustomData_bmesh_set_layer_n(CustomData *data, void *block, const int n, const void *source) { void *dest = CustomData_bmesh_get_layer_n(data, block, n); const LayerTypeInfo *typeInfo = layerType_getInfo(data->layers[n].type); @@ -4273,7 +4353,9 @@ void CustomData_file_write_info(int type, const char **r_struct_name, int *r_str *r_struct_num = typeInfo->structnum; } -void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16> &layers_to_write) +void CustomData_blend_write_prepare(CustomData &data, + Vector<CustomDataLayer, 16> &layers_to_write, + const Set<StringRef> &skip_names) { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { if (layer.flag & CD_FLAG_NOCOPY) { @@ -4282,6 +4364,9 @@ void CustomData_blend_write_prepare(CustomData &data, Vector<CustomDataLayer, 16 if (layer.anonymous_id != nullptr) { continue; } + if (skip_names.contains(layer.name)) { + continue; + } layers_to_write.append(layer); } data.totlayer = layers_to_write.size(); @@ -4328,7 +4413,7 @@ int CustomData_layertype_layers_max(const int type) return typeInfo->layers_max(); } -static bool cd_layer_find_dupe(CustomData *data, const char *name, int type, int index) +static bool cd_layer_find_dupe(CustomData *data, const char *name, const int type, const int index) { /* see if there is a duplicate */ for (int i = 0; i < data->totlayer; i++) { @@ -4363,7 +4448,7 @@ static bool customdata_unique_check(void *arg, const char *name) return cd_layer_find_dupe(data_arg->data, name, data_arg->type, data_arg->index); } -void CustomData_set_layer_unique_name(CustomData *data, int index) +void CustomData_set_layer_unique_name(CustomData *data, const int index) { CustomDataLayer *nlayer = &data->layers[index]; const LayerTypeInfo *typeInfo = layerType_getInfo(nlayer->type); @@ -4408,7 +4493,7 @@ void CustomData_validate_layer_name(const CustomData *data, } } -bool CustomData_verify_versions(CustomData *data, int index) +bool CustomData_verify_versions(CustomData *data, const int index) { const LayerTypeInfo *typeInfo; CustomDataLayer *layer = &data->layers[index]; @@ -4567,7 +4652,7 @@ void CustomData_external_reload(CustomData *data, } } -void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, int totelem) +void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, const int totelem) { CustomDataExternal *external = data->external; CustomDataLayer *layer; @@ -4641,7 +4726,7 @@ void CustomData_external_read(CustomData *data, ID *id, eCustomDataMask mask, in } void CustomData_external_write( - CustomData *data, ID *id, eCustomDataMask mask, int totelem, int free) + CustomData *data, ID *id, eCustomDataMask mask, const int totelem, const int free) { CustomDataExternal *external = data->external; int update = 0; @@ -4743,8 +4828,11 @@ void CustomData_external_write( cdf_free(cdf); } -void CustomData_external_add( - CustomData *data, ID *UNUSED(id), int type, int UNUSED(totelem), const char *filepath) +void CustomData_external_add(CustomData *data, + ID *UNUSED(id), + const int type, + const int UNUSED(totelem), + const char *filepath) { CustomDataExternal *external = data->external; @@ -4768,7 +4856,7 @@ void CustomData_external_add( layer->flag |= CD_FLAG_EXTERNAL | CD_FLAG_IN_MEMORY; } -void CustomData_external_remove(CustomData *data, ID *id, int type, int totelem) +void CustomData_external_remove(CustomData *data, ID *id, const int type, const int totelem) { CustomDataExternal *external = data->external; @@ -4792,7 +4880,7 @@ void CustomData_external_remove(CustomData *data, ID *id, int type, int totelem) } } -bool CustomData_external_test(CustomData *data, int type) +bool CustomData_external_test(CustomData *data, const int type) { int layer_index = CustomData_get_active_layer_index(data, type); if (layer_index == -1) { @@ -5093,7 +5181,10 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap, /** \name Custom Data IO * \{ */ -static void write_mdisps(BlendWriter *writer, int count, const MDisps *mdlist, int external) +static void write_mdisps(BlendWriter *writer, + const int count, + const MDisps *mdlist, + const int external) { if (mdlist) { BLO_write_struct_array(writer, MDisps, count, mdlist); @@ -5193,7 +5284,10 @@ void CustomData_blend_write(BlendWriter *writer, } } -static void blend_read_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external) +static void blend_read_mdisps(BlendDataReader *reader, + const int count, + MDisps *mdisps, + const int external) { if (mdisps) { for (int i = 0; i < count; i++) { @@ -5235,7 +5329,7 @@ static void blend_read_paint_mask(BlendDataReader *reader, } } -void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count) +void CustomData_blend_read(BlendDataReader *reader, CustomData *data, const int count) { BLO_read_data_address(reader, &data->layers); diff --git a/source/blender/blenkernel/intern/data_transfer.c b/source/blender/blenkernel/intern/data_transfer.c index 17a74b5564a..be686635d3e 100644 --- a/source/blender/blenkernel/intern/data_transfer.c +++ b/source/blender/blenkernel/intern/data_transfer.c @@ -70,8 +70,6 @@ void BKE_object_data_transfer_dttypes_to_cdmask(const int dtdata_types, r_data_masks->lmask |= CD_MASK_MLOOPUV; } else if (cddata_type == CD_FAKE_LNOR) { - r_data_masks->vmask |= CD_MASK_NORMAL; - r_data_masks->pmask |= CD_MASK_NORMAL; r_data_masks->lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; } } diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index 823fce52b50..0b3ed584246 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -86,19 +86,6 @@ DispList *BKE_displist_find(ListBase *lb, int type) return nullptr; } -void BKE_displist_copy(ListBase *lbn, const ListBase *lb) -{ - BKE_displist_free(lbn); - - LISTBASE_FOREACH (const DispList *, dl, lb) { - DispList *dln = (DispList *)MEM_dupallocN(dl); - BLI_addtail(lbn, dln); - dln->verts = (float *)MEM_dupallocN(dl->verts); - dln->nors = (float *)MEM_dupallocN(dl->nors); - dln->index = (int *)MEM_dupallocN(dl->index); - } -} - void BKE_displist_normals_add(ListBase *lb) { float *vdata, *ndata, nor[3]; @@ -1493,7 +1480,7 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, * - The dependency graph has handling of edit mode pointers (see #update_edit_mode_pointers) * but it doesn't seem to work in this case. * - * Since the the plan is to replace this legacy curve object with the curves data-block + * Since the plan is to replace this legacy curve object with the curves data-block * (see T95355), this somewhat hacky inefficient solution is relatively temporary. */ Curve &cow_curve = *reinterpret_cast<Curve *>( diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index d68b322e4c5..bf224a9613e 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -337,7 +337,6 @@ static void gpencil_convert_spline(Main *bmain, /* Add stroke to frame. */ BLI_addtail(&gpf->strokes, gps); - float *coord_array = NULL; float init_co[3]; switch (nu->type) { @@ -376,8 +375,7 @@ static void gpencil_convert_spline(Main *bmain, BezTriple *bezt = &nu->bezt[inext]; bool last = (bool)(s == segments - 1); - coord_array = MEM_callocN((size_t)3 * resolu * sizeof(float), __func__); - + float *coord_array = MEM_callocN(sizeof(float[3]) * resolu, __func__); for (int j = 0; j < 3; j++) { BKE_curve_forward_diff_bezier(prevbezt->vec[1][j], prevbezt->vec[2][j], @@ -397,8 +395,9 @@ static void gpencil_convert_spline(Main *bmain, gpencil_add_new_points( gps, coord_array, radius_start, radius_end, init, resolu, init_co, last); + /* Free memory. */ - MEM_SAFE_FREE(coord_array); + MEM_freeN(coord_array); /* As the last point of segment is the first point of next segment, back one array * element to avoid duplicated points on the same location. @@ -419,7 +418,7 @@ static void gpencil_convert_spline(Main *bmain, nurb_points = (nu->pntsu - 1) * resolu; } /* Get all curve points. */ - coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__); + float *coord_array = MEM_callocN(sizeof(float[3]) * nurb_points, __func__); BKE_nurb_makeCurve(nu, coord_array, NULL, NULL, NULL, resolu, sizeof(float[3])); /* Allocate memory for storage points. */ @@ -429,7 +428,7 @@ static void gpencil_convert_spline(Main *bmain, /* Add points. */ gpencil_add_new_points(gps, coord_array, 1.0f, 1.0f, 0, gps->totpoints, init_co, false); - MEM_SAFE_FREE(coord_array); + MEM_freeN(coord_array); } break; } diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 82899b974bc..8ac268b26b0 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -360,7 +360,8 @@ GpencilModifierData *BKE_gpencil_modifier_new(int type) md->type = type; md->mode = eGpencilModifierMode_Realtime | eGpencilModifierMode_Render; md->flag = eGpencilModifierFlag_OverrideLibrary_Local; - md->ui_expand_flag = 1; /* Only expand the parent panel at first. */ + /* Only expand the parent panel at first. */ + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; if (mti->flags & eGpencilModifierTypeFlag_EnableInEditmode) { md->mode |= eGpencilModifierMode_Editmode; diff --git a/source/blender/blenkernel/intern/icons_rasterize.c b/source/blender/blenkernel/intern/icons_rasterize.c index 5603d84022d..00dbdcfa1e5 100644 --- a/source/blender/blenkernel/intern/icons_rasterize.c +++ b/source/blender/blenkernel/intern/icons_rasterize.c @@ -76,7 +76,7 @@ ImBuf *BKE_icon_geom_rasterize(const struct Icon_Geom *geom, const uchar(*pos)[2] = geom->coords; const uint *col = (void *)geom->colors; - /* TODO(campbell): Currently rasterizes to fixed size, then scales. + /* TODO(@campbellbarton): Currently rasterizes to fixed size, then scales. * Should rasterize to double size for eg instead. */ const int rect_size[2] = {max_ii(256, (int)size_x * 2), max_ii(256, (int)size_y * 2)}; diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 43e732b428d..98c317c547b 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -784,7 +784,7 @@ IDProperty *IDP_GetProperties(ID *id, const bool create_if_needed) if (create_if_needed) { id->properties = MEM_callocN(sizeof(IDProperty), "IDProperty"); id->properties->type = IDP_GROUP; - /* NOTE(campbell): Don't overwrite the data's name and type + /* NOTE(@campbellbarton): Don't overwrite the data's name and type * some functions might need this if they * don't have a real ID, should be named elsewhere. */ // strcpy(id->name, "top_level_group"); diff --git a/source/blender/blenkernel/intern/idprop_create.cc b/source/blender/blenkernel/intern/idprop_create.cc index f549393fd12..a2f58baebf7 100644 --- a/source/blender/blenkernel/intern/idprop_create.cc +++ b/source/blender/blenkernel/intern/idprop_create.cc @@ -44,6 +44,14 @@ std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_n return std::unique_ptr<IDProperty, IDPropertyDeleter>(property); } +std::unique_ptr<IDProperty, IDPropertyDeleter> create(const StringRefNull prop_name, ID *value) +{ + IDPropertyTemplate prop_template{0}; + prop_template.id = value; + IDProperty *property = IDP_New(IDP_ID, &prop_template, prop_name.c_str()); + return std::unique_ptr<IDProperty, IDPropertyDeleter>(property); +} + static std::unique_ptr<IDProperty, IDPropertyDeleter> array_create(const StringRefNull prop_name, eIDPropertyType subtype, size_t array_len) diff --git a/source/blender/blenkernel/intern/image.cc b/source/blender/blenkernel/intern/image.cc index 78eaba44a08..3e5d997c873 100644 --- a/source/blender/blenkernel/intern/image.cc +++ b/source/blender/blenkernel/intern/image.cc @@ -627,6 +627,16 @@ void BKE_image_free_data(Image *ima) image_free_data(&ima->id); } +static ImageTile *imagetile_alloc(int tile_number) +{ + ImageTile *tile = MEM_cnew<ImageTile>("Image Tile"); + tile->tile_number = tile_number; + tile->gen_x = 1024; + tile->gen_y = 1024; + tile->gen_type = IMA_GENTYPE_GRID; + return tile; +} + /* only image block itself */ static void image_init(Image *ima, short source, short type) { @@ -641,8 +651,7 @@ static void image_init(Image *ima, short source, short type) ima->flag |= IMA_VIEW_AS_RENDER; } - ImageTile *tile = MEM_cnew<ImageTile>("Image Tiles"); - tile->tile_number = 1001; + ImageTile *tile = imagetile_alloc(1001); BLI_addtail(&ima->tiles, tile); if (type == IMA_TYPE_R_RESULT) { @@ -863,37 +872,89 @@ void BKE_image_get_tile_uv(const Image *ima, const int tile_number, float r_uv[2 } } +/** Linear distance between #x and the unit interval. */ +static float distance_to_unit_interval(float x) +{ + /* The unit interval is between 0 and 1. + * Within the interval, return 0. + * Outside the interval, return the distance to the nearest boundary. + * Intuitively, the function looks like: + * \ | | / + * __\|___|/__ + * 0 1 + */ + + if (x <= 0.0f) { + return -x; /* Distance to left border. */ + } + if (x <= 1.0f) { + return 0.0f; /* Inside unit interval. */ + } + return x - 1.0f; /* Distance to right border. */ +} + +/** Distance squared between #co and the unit square with lower-left starting at #udim. */ +static float distance_squared_to_udim(const float co[2], const float udim[2]) +{ + float delta[2]; + sub_v2_v2v2(delta, co, udim); + delta[0] = distance_to_unit_interval(delta[0]); + delta[1] = distance_to_unit_interval(delta[1]); + return len_squared_v2(delta); +} + +static bool nearest_udim_tile_tie_break(const float best_dist_sq, + const float best_uv[2], + const float dist_sq, + const float uv[2]) +{ + if (best_dist_sq == dist_sq) { /* Exact same distance? Tie-break. */ + if (best_uv[0] == uv[0]) { /* Exact same U? Tie-break. */ + return (uv[1] > best_uv[1]); /* Higher than previous candidate? */ + } + return (uv[0] > best_uv[0]); /* Further right than previous candidate? */ + } + return (dist_sq < best_dist_sq); /* Closer than previous candidate? */ +} + int BKE_image_find_nearest_tile_with_offset(const Image *image, const float co[2], float r_uv_offset[2]) { - /* Distance squared to the closest UDIM tile. */ - float dist_best_sq = FLT_MAX; - float uv_offset_best[2] = {0, 0}; + /* NOTE: If the co-ordinates are integers, take special care to break ties. */ + + zero_v2(r_uv_offset); int tile_number_best = -1; - const float co_offset[2] = {co[0] - 0.5f, co[1] - 0.5f}; + if (!image || image->source != IMA_SRC_TILED) { + return tile_number_best; + } + + /* Distance squared to the closest UDIM tile. */ + float dist_best_sq = FLT_MAX; LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { float uv_offset[2]; BKE_image_get_tile_uv(image, tile->tile_number, uv_offset); - /* Distance squared between co[2] and center of UDIM tile. */ - const float dist_sq = len_squared_v2v2(uv_offset, co_offset); + /* Distance squared between #co and closest point on UDIM tile. */ + const float dist_sq = distance_squared_to_udim(co, uv_offset); - if (dist_sq < dist_best_sq) { + if (dist_sq == 0) { /* Either inside in the UDIM, or on its boundary. */ + if (floorf(co[0]) == uv_offset[0] && floorf(co[1]) == uv_offset[1]) { + /* Within the half-open interval of the UDIM. */ + copy_v2_v2(r_uv_offset, uv_offset); + return tile_number_best; + } + } + + if (nearest_udim_tile_tie_break(dist_best_sq, r_uv_offset, dist_sq, uv_offset)) { + /* Tile is better than previous best, update. */ dist_best_sq = dist_sq; + copy_v2_v2(r_uv_offset, uv_offset); tile_number_best = tile->tile_number; - copy_v2_v2(uv_offset_best, uv_offset); - - if (dist_best_sq < 0.5f * 0.5f) { - break; /* No other tile can be closer. */ - } } } - if (tile_number_best != -1) { - copy_v2_v2(r_uv_offset, uv_offset_best); - } return tile_number_best; } @@ -910,7 +971,7 @@ static void image_init_color_management(Image *ima) BKE_image_user_file_path(nullptr, ima, name); - /* will set input color space to image format default's */ + /* Will set input color space to image format default's. */ ibuf = IMB_loadiffname(name, IB_test | IB_alphamode_detect, ima->colorspace_settings.name); if (ibuf) { @@ -1048,73 +1109,70 @@ static void image_buf_fill_isolated(void *usersata_v) } } -static ImBuf *add_ibuf_size(unsigned int width, - unsigned int height, - const char *name, - int depth, - int floatbuf, - short gen_type, - const float color[4], - ColorManagedColorspaceSettings *colorspace_settings) +static ImBuf *add_ibuf_for_tile(Image *ima, ImageTile *tile) { ImBuf *ibuf; unsigned char *rect = nullptr; float *rect_float = nullptr; float fill_color[4]; + const bool floatbuf = (tile->gen_flag & IMA_GEN_FLOAT) != 0; if (floatbuf) { - ibuf = IMB_allocImBuf(width, height, depth, IB_rectfloat); + ibuf = IMB_allocImBuf(tile->gen_x, tile->gen_y, tile->gen_depth, IB_rectfloat); - if (colorspace_settings->name[0] == '\0') { + if (ima->colorspace_settings.name[0] == '\0') { const char *colorspace = IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_DEFAULT_FLOAT); - STRNCPY(colorspace_settings->name, colorspace); + STRNCPY(ima->colorspace_settings.name, colorspace); } if (ibuf != nullptr) { rect_float = ibuf->rect_float; - IMB_colormanagement_check_is_data(ibuf, colorspace_settings->name); + IMB_colormanagement_check_is_data(ibuf, ima->colorspace_settings.name); } - if (IMB_colormanagement_space_name_is_data(colorspace_settings->name)) { - copy_v4_v4(fill_color, color); + if (IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) { + copy_v4_v4(fill_color, tile->gen_color); } else { /* The input color here should ideally be linear already, but for now * we just convert and postpone breaking the API for later. */ - srgb_to_linearrgb_v4(fill_color, color); + srgb_to_linearrgb_v4(fill_color, tile->gen_color); } } else { - ibuf = IMB_allocImBuf(width, height, depth, IB_rect); + ibuf = IMB_allocImBuf(tile->gen_x, tile->gen_y, tile->gen_depth, IB_rect); - if (colorspace_settings->name[0] == '\0') { + if (ima->colorspace_settings.name[0] == '\0') { const char *colorspace = IMB_colormanagement_role_colorspace_name_get( COLOR_ROLE_DEFAULT_BYTE); - STRNCPY(colorspace_settings->name, colorspace); + STRNCPY(ima->colorspace_settings.name, colorspace); } if (ibuf != nullptr) { rect = (unsigned char *)ibuf->rect; - IMB_colormanagement_assign_rect_colorspace(ibuf, colorspace_settings->name); + IMB_colormanagement_assign_rect_colorspace(ibuf, ima->colorspace_settings.name); } - copy_v4_v4(fill_color, color); + copy_v4_v4(fill_color, tile->gen_color); } if (!ibuf) { return nullptr; } - STRNCPY(ibuf->name, name); + STRNCPY(ibuf->name, ima->filepath); + + /* Mark the tile itself as having been generated. */ + tile->gen_flag |= IMA_GEN_TILE; ImageFillData data; - data.gen_type = gen_type; - data.width = width; - data.height = height; + data.gen_type = tile->gen_type; + data.width = tile->gen_x; + data.height = tile->gen_y; data.rect = rect; data.rect_float = rect_float; copy_v4_v4(data.fill_color, fill_color); @@ -1153,12 +1211,16 @@ Image *BKE_image_add_generated(Main *bmain, /* NOTE: leave `ima->filepath` unset, * setting it to a dummy value may write to an invalid file-path. */ - ima->gen_x = width; - ima->gen_y = height; - ima->gen_type = gen_type; - ima->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0); - ima->gen_depth = depth; - copy_v4_v4(ima->gen_color, color); + + /* The generation info is always stored in the tiles. The first tile + * will be used for non-tiled images. */ + ImageTile *tile = static_cast<ImageTile *>(ima->tiles.first); + tile->gen_x = width; + tile->gen_y = height; + tile->gen_type = gen_type; + tile->gen_flag |= (floatbuf ? IMA_GEN_FLOAT : 0); + tile->gen_depth = depth; + copy_v4_v4(tile->gen_color, color); if (is_data) { STRNCPY(ima->colorspace_settings.name, @@ -1167,8 +1229,7 @@ Image *BKE_image_add_generated(Main *bmain, for (view_id = 0; view_id < 2; view_id++) { ImBuf *ibuf; - ibuf = add_ibuf_size( - width, height, ima->filepath, depth, floatbuf, gen_type, color, &ima->colorspace_settings); + ibuf = add_ibuf_for_tile(ima, tile); int index = tiled ? 0 : IMA_NO_INDEX; int entry = tiled ? 1001 : 0; image_assign_ibuf(ima, ibuf, stereo3d ? view_id : index, entry); @@ -2949,6 +3010,28 @@ static void image_free_tile(Image *ima, ImageTile *tile) } } +static bool image_remove_tile(Image *ima, ImageTile *tile) +{ + if (BLI_listbase_is_single(&ima->tiles)) { + /* Can't remove the last remaining tile. */ + return false; + } + + image_free_tile(ima, tile); + BLI_remlink(&ima->tiles, tile); + MEM_freeN(tile); + + return true; +} + +static void image_remove_all_tiles(Image *ima) +{ + /* Remove all but the final tile. */ + while (image_remove_tile(ima, static_cast<ImageTile *>(ima->tiles.last))) { + ; + } +} + void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) { if (ima == nullptr) { @@ -2975,11 +3058,12 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) } if (ima->source == IMA_SRC_GENERATED) { - if (ima->gen_x == 0 || ima->gen_y == 0) { + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + if (base_tile->gen_x == 0 || base_tile->gen_y == 0) { ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, nullptr); if (ibuf) { - ima->gen_x = ibuf->x; - ima->gen_y = ibuf->y; + base_tile->gen_x = ibuf->x; + base_tile->gen_y = ibuf->y; IMB_freeImBuf(ibuf); } } @@ -2996,16 +3080,40 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) if (ima->source != IMA_SRC_TILED) { /* Free all but the first tile. */ + image_remove_all_tiles(ima); + + /* If the remaining tile is generated, we need to again ensure that we + * wouldn't continue to use the old filepath. + * + * Otherwise, if this used to be a UDIM image, get the concrete filepath associated + * with the remaining tile and use that as the new filepath. */ ImageTile *base_tile = BKE_image_get_tile(ima, 0); - BLI_assert(base_tile == ima->tiles.first); - for (ImageTile *tile = base_tile->next, *tile_next; tile; tile = tile_next) { - tile_next = tile->next; - image_free_tile(ima, tile); - MEM_freeN(tile); + if ((base_tile->gen_flag & IMA_GEN_TILE) != 0) { + ima->filepath[0] = '\0'; + } + else if (BKE_image_is_filename_tokenized(ima->filepath)) { + const bool was_relative = BLI_path_is_rel(ima->filepath); + + eUDIM_TILE_FORMAT tile_format; + char *udim_pattern = BKE_image_get_tile_strformat(ima->filepath, &tile_format); + BKE_image_set_filepath_from_tile_number( + ima->filepath, udim_pattern, tile_format, base_tile->tile_number); + MEM_freeN(udim_pattern); + + if (was_relative) { + const char *relbase = ID_BLEND_PATH(bmain, &ima->id); + BLI_path_rel(ima->filepath, relbase); + } } - base_tile->next = nullptr; + + /* If the remaining tile was not number 1001, we need to reassign it so that + * ibuf lookups from the cache still succeed. */ base_tile->tile_number = 1001; - ima->tiles.last = base_tile; + } + else { + /* When changing to UDIM, attempt to tokenize the filepath. */ + char *filename = (char *)BLI_path_basename(ima->filepath); + BKE_image_ensure_tile_token(filename); } /* image buffers for non-sequence multilayer will share buffers with RenderResult, @@ -3021,6 +3129,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) image_tag_frame_recalc(ima, nullptr, iuser, ima); } BKE_image_walk_all_users(bmain, ima, image_tag_frame_recalc); + BKE_image_partial_update_mark_full_update(ima); break; @@ -3072,9 +3181,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) * to account for how the two sets might or might not overlap. To be complete, we start * the refresh process by clearing all existing tiles, stopping when there's only 1 tile * left. */ - while (BKE_image_remove_tile(ima, static_cast<ImageTile *>(ima->tiles.last))) { - ; - } + image_remove_all_tiles(ima); int remaining_tile_number = ((ImageTile *)ima->tiles.first)->tile_number; bool needs_final_cleanup = true; @@ -3257,8 +3364,7 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } } - ImageTile *tile = MEM_cnew<ImageTile>("image new tile"); - tile->tile_number = tile_number; + ImageTile *tile = imagetile_alloc(tile_number); if (next_tile) { BLI_insertlinkbefore(&ima->tiles, next_tile, tile); @@ -3293,16 +3399,7 @@ bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) return false; } - if (BLI_listbase_is_single(&ima->tiles)) { - /* Can't remove the last remaining tile. */ - return false; - } - - image_free_tile(ima, tile); - BLI_remlink(&ima->tiles, tile); - MEM_freeN(tile); - - return true; + return image_remove_tile(ima, tile); } void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_number) @@ -3364,14 +3461,7 @@ void BKE_image_sort_tiles(struct Image *ima) BLI_listbase_sort(&ima->tiles, tile_sort_cb); } -bool BKE_image_fill_tile(struct Image *ima, - ImageTile *tile, - int width, - int height, - const float color[4], - int gen_type, - int planes, - bool is_float) +bool BKE_image_fill_tile(struct Image *ima, ImageTile *tile) { if (ima == nullptr || tile == nullptr || ima->source != IMA_SRC_TILED) { return false; @@ -3379,8 +3469,7 @@ bool BKE_image_fill_tile(struct Image *ima, image_free_tile(ima, tile); - ImBuf *tile_ibuf = add_ibuf_size( - width, height, ima->filepath, planes, is_float, gen_type, color, &ima->colorspace_settings); + ImBuf *tile_ibuf = add_ibuf_for_tile(ima, tile); if (tile_ibuf != nullptr) { image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number); @@ -4553,14 +4642,22 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) } } else if (ima->source == IMA_SRC_TILED) { - if (ima->type == IMA_TYPE_IMAGE) { - /* Regular files, ibufs in flip-book, allows saving */ - ibuf = image_load_image_file(ima, iuser, entry, 0, false); + /* Nothing was cached. Check to see if the tile should be generated. */ + ImageTile *tile = BKE_image_get_tile(ima, entry); + if ((tile->gen_flag & IMA_GEN_TILE) != 0) { + ibuf = add_ibuf_for_tile(ima, tile); + image_assign_ibuf(ima, ibuf, 0, entry); } - /* no else; on load the ima type can change */ - if (ima->type == IMA_TYPE_MULTILAYER) { - /* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */ - ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0); + else { + if (ima->type == IMA_TYPE_IMAGE) { + /* Regular files, ibufs in flip-book, allows saving */ + ibuf = image_load_image_file(ima, iuser, entry, 0, false); + } + /* no else; on load the ima type can change */ + if (ima->type == IMA_TYPE_MULTILAYER) { + /* Only 1 layer/pass stored in imbufs, no EXR-handle anim storage, no saving. */ + ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0); + } } } else if (ima->source == IMA_SRC_FILE) { @@ -4578,23 +4675,17 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) else if (ima->source == IMA_SRC_GENERATED) { /* Generated is: `ibuf` is allocated dynamically. */ /* UV test-grid or black or solid etc. */ - if (ima->gen_x == 0) { - ima->gen_x = 1024; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + if (base_tile->gen_x == 0) { + base_tile->gen_x = 1024; } - if (ima->gen_y == 0) { - ima->gen_y = 1024; + if (base_tile->gen_y == 0) { + base_tile->gen_y = 1024; } - if (ima->gen_depth == 0) { - ima->gen_depth = 24; + if (base_tile->gen_depth == 0) { + base_tile->gen_depth = 24; } - ibuf = add_ibuf_size(ima->gen_x, - ima->gen_y, - ima->filepath, - ima->gen_depth, - (ima->gen_flag & IMA_GEN_FLOAT) != 0, - ima->gen_type, - ima->gen_color, - &ima->colorspace_settings); + ibuf = add_ibuf_for_tile(ima, base_tile); image_assign_ibuf(ima, ibuf, index, 0); } else if (ima->source == IMA_SRC_VIEWER) { diff --git a/source/blender/blenkernel/intern/image_save.cc b/source/blender/blenkernel/intern/image_save.cc index 9d23af39ec3..d366e9362e8 100644 --- a/source/blender/blenkernel/intern/image_save.cc +++ b/source/blender/blenkernel/intern/image_save.cc @@ -316,6 +316,8 @@ static void image_save_post(ReportList *reports, if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { ima->source = IMA_SRC_FILE; ima->type = IMA_TYPE_IMAGE; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_flag &= ~IMA_GEN_TILE; } /* Update image file color space when saving to another color space. */ @@ -662,8 +664,11 @@ bool BKE_image_save( } } - /* Set the image path only if all tiles were ok. */ + /* Set the image path and clear the per-tile generated flag only if all tiles were ok. */ if (ok) { + LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { + tile->gen_flag &= ~IMA_GEN_TILE; + } image_save_update_filepath(ima, opts->filepath, opts); } MEM_freeN(udim_pattern); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 3778e308db6..5a394a05d86 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -59,6 +59,7 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "RNA_access.h" @@ -708,6 +709,58 @@ ID *BKE_id_copy_for_duplicate(Main *bmain, return id->newid; } +static int foreach_assign_id_to_orig_callback(LibraryIDLinkCallbackData *cb_data) +{ + ID **id_p = cb_data->id_pointer; + + if (*id_p) { + ID *id = *id_p; + *id_p = DEG_get_original_id(id); + + /* If the ID changes increase the user count. + * + * This means that the reference to evaluated ID has been changed with a reference to the + * original ID which implies that the user count of the original ID is increased. + * + * The evaluated IDs do not maintain their user counter, so do not change it to avoid issues + * with the user counter going negative. */ + if (*id_p != id) { + if ((cb_data->cb_flag & IDWALK_CB_USER) != 0) { + id_us_plus(*id_p); + } + } + } + + return IDWALK_RET_NOP; +} + +ID *BKE_id_copy_for_use_in_bmain(Main *bmain, const ID *id) +{ + ID *newid = BKE_id_copy(bmain, id); + + if (newid == NULL) { + return newid; + } + + /* Assign ID references directly used by the given ID to their original complementary parts. + * + * For example, when is called on an evaluated object will assign object->data to its original + * pointer, the evaluated object->data will be kept unchanged. */ + BKE_library_foreach_ID_link(NULL, newid, foreach_assign_id_to_orig_callback, NULL, IDWALK_NOP); + + /* Shape keys reference on evaluated ID is preserved to keep driver paths available, but the key + * data is likely to be invalid now due to modifiers, so clear the shape key reference avoiding + * any possible shape corruption. */ + if (DEG_is_evaluated_id(id)) { + Key **key_p = BKE_key_from_id_p(newid); + if (key_p) { + *key_p = NULL; + } + } + + return newid; +} + /** * Does a mere memory swap over the whole IDs data (including type-specific memory). * \note Most internal ID data itself is not swapped (only IDProperties are). diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 6b9d2afe87a..38d1a30592d 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -696,8 +696,8 @@ static void lib_query_unused_ids_tag_recurse(Main *bmain, bool has_valid_from_users = false; /* Preemptively consider this ID as unused. That way if there is a loop of dependency leading * back to it, it won't create a fake 'valid user' detection. - * NOTE: This can only only be done for a subset of IDs, some types are never 'indirectly - * unused', same for IDs with a fake user. */ + * NOTE: This can only be done for a subset of IDs, some types are never 'indirectly unused', + * same for IDs with a fake user. */ if ((id->flag & LIB_FAKEUSER) == 0 && !ELEM(GS(id->name), ID_SCE, ID_WM, ID_SCR, ID_WS, ID_LI)) { id->tag |= tag; } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index dd58c9cc4fe..9424b615031 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -122,7 +122,7 @@ void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath) /* Not essential but set `filepath_abs` is an absolute copy of value which * is more useful if its kept in sync. */ if (BLI_path_is_rel(lib->filepath_abs)) { - /* NOTE(campbell): the file may be unsaved, in this case, setting the + /* NOTE(@campbellbarton): the file may be unsaved, in this case, setting the * `filepath_abs` on an indirectly linked path is not allowed from the * outliner, and its not really supported but allow from here for now * since making local could cause this to be directly linked. diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index f899901b54e..248d292664a 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -852,7 +852,7 @@ void BKE_object_material_resize(Main *bmain, Object *ob, const short totcol, boo ob->mat = newmatar; ob->matbits = newmatbits; } - /* XXX(campbell): why not realloc on shrink? */ + /* XXX(@campbellbarton): why not realloc on shrink? */ ob->totcol = totcol; if (ob->totcol && ob->actcol == 0) { diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.cc index 2a1c940493c..084fea6abbd 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.cc @@ -11,12 +11,12 @@ * texture coordinates are patched within the displist */ -#include <ctype.h> -#include <float.h> -#include <math.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <cctype> +#include <cfloat> +#include <cmath> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -72,11 +72,11 @@ static void metaball_copy_data(Main *UNUSED(bmain), BLI_duplicatelist(&metaball_dst->elems, &metaball_src->elems); - metaball_dst->mat = MEM_dupallocN(metaball_src->mat); + metaball_dst->mat = static_cast<Material **>(MEM_dupallocN(metaball_src->mat)); - metaball_dst->editelems = NULL; - metaball_dst->lastelem = NULL; - metaball_dst->batch_cache = NULL; + metaball_dst->editelems = nullptr; + metaball_dst->lastelem = nullptr; + metaball_dst->batch_cache = nullptr; } static void metaball_free_data(ID *id) @@ -107,11 +107,11 @@ static void metaball_blend_write(BlendWriter *writer, ID *id, const void *id_add /* Clean up, important in undo case to reduce false detection of changed datablocks. */ BLI_listbase_clear(&mb->disp); - mb->editelems = NULL; + mb->editelems = nullptr; /* Must always be cleared (meta's don't have their own edit-data). */ mb->needs_flush_to_id = 0; - mb->lastelem = NULL; - mb->batch_cache = NULL; + mb->lastelem = nullptr; + mb->batch_cache = nullptr; /* write LibData */ BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); @@ -139,12 +139,12 @@ static void metaball_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_list(reader, &(mb->elems)); BLI_listbase_clear(&mb->disp); - mb->editelems = NULL; + mb->editelems = nullptr; /* Must always be cleared (meta's don't have their own edit-data). */ mb->needs_flush_to_id = 0; - // mb->edit_elems.first = mb->edit_elems.last = NULL; - mb->lastelem = NULL; - mb->batch_cache = NULL; + // mb->edit_elems.first = mb->edit_elems.last = nullptr; + mb->lastelem = nullptr; + mb->batch_cache = nullptr; } static void metaball_blend_read_lib(BlendLibReader *reader, ID *id) @@ -166,49 +166,46 @@ static void metaball_blend_read_expand(BlendExpander *expander, ID *id) } IDTypeInfo IDType_ID_MB = { - .id_code = ID_MB, - .id_filter = FILTER_ID_MB, - .main_listbase_index = INDEX_ID_MB, - .struct_size = sizeof(MetaBall), - .name = "Metaball", - .name_plural = "metaballs", - .translation_context = BLT_I18NCONTEXT_ID_METABALL, - .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, - .asset_type_info = NULL, - - .init_data = metaball_init_data, - .copy_data = metaball_copy_data, - .free_data = metaball_free_data, - .make_local = NULL, - .foreach_id = metaball_foreach_id, - .foreach_cache = NULL, - .foreach_path = NULL, - .owner_get = NULL, - - .blend_write = metaball_blend_write, - .blend_read_data = metaball_blend_read_data, - .blend_read_lib = metaball_blend_read_lib, - .blend_read_expand = metaball_blend_read_expand, - - .blend_read_undo_preserve = NULL, - - .lib_override_apply_post = NULL, + /* id_code */ ID_MB, + /* id_filter */ FILTER_ID_MB, + /* main_listbase_index */ INDEX_ID_MB, + /* struct_size */ sizeof(MetaBall), + /* name */ "Metaball", + /* name_plural */ "metaballs", + /* translation_context */ BLT_I18NCONTEXT_ID_METABALL, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, + /* asset_type_info */ nullptr, + + /* init_data */ metaball_init_data, + /* copy_data */ metaball_copy_data, + /* free_data */ metaball_free_data, + /* make_local */ nullptr, + /* foreach_id */ metaball_foreach_id, + /* foreach_cache */ nullptr, + /* foreach_path */ nullptr, + /* owner_get */ nullptr, + + /* blend_write */ metaball_blend_write, + /* blend_read_data */ metaball_blend_read_data, + /* blend_read_lib */ metaball_blend_read_lib, + /* blend_read_expand */ metaball_blend_read_expand, + + /* blend_read_undo_preserve */ nullptr, + + /* lib_override_apply_post */ nullptr, }; /* Functions */ MetaBall *BKE_mball_add(Main *bmain, const char *name) { - MetaBall *mb; - - mb = BKE_id_new(bmain, ID_MB, name); - + MetaBall *mb = static_cast<MetaBall *>(BKE_id_new(bmain, ID_MB, name)); return mb; } MetaElem *BKE_mball_element_add(MetaBall *mb, const int type) { - MetaElem *ml = MEM_callocN(sizeof(MetaElem), "metaelem"); + MetaElem *ml = MEM_cnew<MetaElem>(__func__); unit_qt(ml->quat); @@ -260,8 +257,8 @@ void BKE_mball_texspace_calc(Object *ob) int tot; bool do_it = false; - if (ob->runtime.bb == NULL) { - ob->runtime.bb = MEM_callocN(sizeof(BoundBox), "mb boundbox"); + if (ob->runtime.bb == nullptr) { + ob->runtime.bb = MEM_cnew<BoundBox>(__func__); } bb = ob->runtime.bb; @@ -270,7 +267,7 @@ void BKE_mball_texspace_calc(Object *ob) (min)[0] = (min)[1] = (min)[2] = 1.0e30f; (max)[0] = (max)[1] = (max)[2] = -1.0e30f; - dl = ob->runtime.curve_cache->disp.first; + dl = static_cast<DispList *>(ob->runtime.curve_cache->disp.first); while (dl) { tot = dl->nr; if (tot) { @@ -299,13 +296,13 @@ BoundBox *BKE_mball_boundbox_get(Object *ob) { BLI_assert(ob->type == OB_MBALL); - if (ob->runtime.bb != NULL && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { + if (ob->runtime.bb != nullptr && (ob->runtime.bb->flag & BOUNDBOX_DIRTY) == 0) { return ob->runtime.bb; } /* This should always only be called with evaluated objects, * but currently RNA is a problem here... */ - if (ob->runtime.curve_cache != NULL) { + if (ob->runtime.curve_cache != nullptr) { BKE_mball_texspace_calc(ob); } @@ -329,8 +326,8 @@ float *BKE_mball_make_orco(Object *ob, ListBase *dispbase) loc[2] = (bb->vec[0][2] + bb->vec[1][2]) / 2.0f; size[2] = bb->vec[1][2] - loc[2]; - dl = dispbase->first; - orcodata = MEM_mallocN(sizeof(float[3]) * dl->nr, "MballOrco"); + dl = static_cast<DispList *>(dispbase->first); + orcodata = static_cast<float *>(MEM_mallocN(sizeof(float[3]) * dl->nr, __func__)); data = dl->verts; orco = orcodata; @@ -393,7 +390,7 @@ bool BKE_mball_is_basis_for(const Object *ob1, const Object *ob2) bool BKE_mball_is_any_selected(const MetaBall *mb) { - for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) { + LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) { if (ml->flag & SELECT) { return true; } @@ -415,7 +412,7 @@ bool BKE_mball_is_any_selected_multi(Base **bases, int bases_len) bool BKE_mball_is_any_unselected(const MetaBall *mb) { - for (const MetaElem *ml = mb->editelems->first; ml != NULL; ml = ml->next) { + LISTBASE_FOREACH (const MetaElem *, ml, mb->editelems) { if ((ml->flag & SELECT) == 0) { return true; } @@ -451,9 +448,10 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) * Solving this case would drastically increase the complexity of this code though, so don't * think it would be worth it. */ - for (Object *ob_src = bmain->objects.first; ob_src != NULL && !ID_IS_LINKED(ob_src);) { + for (Object *ob_src = static_cast<Object *>(bmain->objects.first); + ob_src != nullptr && !ID_IS_LINKED(ob_src);) { if (ob_src->data != metaball_src) { - ob_src = ob_src->id.next; + ob_src = static_cast<Object *>(ob_src->id.next); continue; } @@ -466,12 +464,13 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) * Using this, it is possible to process the whole set of meta-balls with a single loop on the * whole list of Objects, though additionally going backward on part of the list in some cases. */ - Object *ob_iter = NULL; + Object *ob_iter = nullptr; int obactive_nr, ob_nr; char obactive_name[MAX_ID_NAME], ob_name[MAX_ID_NAME]; BLI_split_name_num(obactive_name, &obactive_nr, ob_src->id.name + 2, '.'); - for (ob_iter = ob_src->id.prev; ob_iter != NULL; ob_iter = ob_iter->id.prev) { + for (ob_iter = static_cast<Object *>(ob_src->id.prev); ob_iter != nullptr; + ob_iter = static_cast<Object *>(ob_iter->id.prev)) { if (ob_iter->id.name[2] != obactive_name[0]) { break; } @@ -483,10 +482,11 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) break; } - mball_data_properties_copy(ob_iter->data, metaball_src); + mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src); } - for (ob_iter = ob_src->id.next; ob_iter != NULL; ob_iter = ob_iter->id.next) { + for (ob_iter = static_cast<Object *>(ob_src->id.next); ob_iter != nullptr; + ob_iter = static_cast<Object *>(ob_iter->id.next)) { if (ob_iter->id.name[2] != obactive_name[0] || ID_IS_LINKED(ob_iter)) { break; } @@ -498,7 +498,7 @@ void BKE_mball_properties_copy(Main *bmain, MetaBall *metaball_src) break; } - mball_data_properties_copy(ob_iter->data, metaball_src); + mball_data_properties_copy(static_cast<MetaBall *>(ob_iter->data), metaball_src); } ob_src = ob_iter; @@ -556,7 +556,7 @@ bool BKE_mball_minmax_ex( copy_v3_v3(centroid, &ml->x); } - /* TODO(campbell): non circle shapes cubes etc, probably nobody notices. */ + /* TODO(@campbellbarton): non circle shapes cubes etc, probably nobody notices. */ for (int i = -1; i != 3; i += 2) { copy_v3_v3(vec, centroid); add_v3_fl(vec, scale_mb * i); @@ -682,7 +682,7 @@ bool BKE_mball_select_all_multi_ex(Base **bases, int bases_len) bool changed_multi = false; for (uint ob_index = 0; ob_index < bases_len; ob_index++) { Object *obedit = bases[ob_index]->object; - MetaBall *mb = obedit->data; + MetaBall *mb = static_cast<MetaBall *>(obedit->data); changed_multi |= BKE_mball_select_all(mb); } return changed_multi; @@ -705,7 +705,7 @@ bool BKE_mball_deselect_all_multi_ex(Base **bases, int bases_len) bool changed_multi = false; for (uint ob_index = 0; ob_index < bases_len; ob_index++) { Object *obedit = bases[ob_index]->object; - MetaBall *mb = obedit->data; + MetaBall *mb = static_cast<MetaBall *>(obedit->data); changed_multi |= BKE_mball_deselect_all(mb); DEG_id_tag_update(&mb->id, ID_RECALC_SELECT); } @@ -737,8 +737,8 @@ bool BKE_mball_select_swap_multi_ex(Base **bases, int bases_len) /* Draw Engine */ -void (*BKE_mball_batch_cache_dirty_tag_cb)(MetaBall *mb, int mode) = NULL; -void (*BKE_mball_batch_cache_free_cb)(MetaBall *mb) = NULL; +void (*BKE_mball_batch_cache_dirty_tag_cb)(MetaBall *mb, int mode) = nullptr; +void (*BKE_mball_batch_cache_free_cb)(MetaBall *mb) = nullptr; void BKE_mball_batch_cache_dirty_tag(MetaBall *mb, int mode) { diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index cf05dc0404e..abf47acd5cc 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -28,6 +28,7 @@ #include "BLI_math.h" #include "BLI_math_vector.hh" #include "BLI_memarena.h" +#include "BLI_span.hh" #include "BLI_string.h" #include "BLI_task.hh" #include "BLI_utildefines.h" @@ -36,6 +37,7 @@ #include "BLT_translation.h" #include "BKE_anim_data.h" +#include "BKE_attribute.hh" #include "BKE_bpath.h" #include "BKE_deform.h" #include "BKE_editmesh.h" @@ -62,6 +64,8 @@ #include "BLO_read_write.h" using blender::float3; +using blender::MutableSpan; +using blender::VArray; using blender::Vector; static void mesh_clear_geometry(Mesh *mesh); @@ -241,10 +245,14 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address memset(&mesh->pdata, 0, sizeof(mesh->pdata)); } else { - CustomData_blend_write_prepare(mesh->vdata, vert_layers); - CustomData_blend_write_prepare(mesh->edata, edge_layers); + if (!BLO_write_is_undo(writer)) { + BKE_mesh_legacy_convert_hide_layers_to_flags(mesh); + } + + CustomData_blend_write_prepare(mesh->vdata, vert_layers, {".hide_vert"}); + CustomData_blend_write_prepare(mesh->edata, edge_layers, {".hide_edge"}); CustomData_blend_write_prepare(mesh->ldata, loop_layers); - CustomData_blend_write_prepare(mesh->pdata, poly_layers); + CustomData_blend_write_prepare(mesh->pdata, poly_layers, {".hide_poly"}); } BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); @@ -323,6 +331,10 @@ static void mesh_blend_read_data(BlendDataReader *reader, ID *id) } } + if (!BLO_read_data_is_undo(reader)) { + BKE_mesh_legacy_convert_flags_to_hide_layers(mesh); + } + /* We don't expect to load normals from files, since they are derived data. */ BKE_mesh_normals_tag_dirty(mesh); BKE_mesh_assert_normals_dirty_or_calculated(mesh); @@ -760,10 +772,10 @@ static void mesh_ensure_tessellation_customdata(Mesh *me) /* TODO: add some `--debug-mesh` option. */ if (G.debug & G_DEBUG) { - /* NOTE(campbell): this warning may be un-called for if we are initializing the mesh for - * the first time from #BMesh, rather than giving a warning about this we could be smarter - * and check if there was any data to begin with, for now just print the warning with - * some info to help troubleshoot what's going on. */ + /* NOTE(@campbellbarton): this warning may be un-called for if we are initializing the mesh + * for the first time from #BMesh, rather than giving a warning about this we could be + * smarter and check if there was any data to begin with, for now just print the warning + * with some info to help troubleshoot what's going on. */ printf( "%s: warning! Tessellation uvs or vcol data got out of sync, " "had to reset!\n CD_MTFACE: %d != CD_MLOOPUV: %d || CD_MCOL: %d != " diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 81bab9f796f..24b81bce784 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -22,6 +22,7 @@ #include "BLI_index_range.hh" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_span.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -54,6 +55,8 @@ #include "DEG_depsgraph_query.h" using blender::IndexRange; +using blender::MutableSpan; +using blender::Span; /* Define for cases when you want extra validation of mesh * after certain modifications. @@ -127,29 +130,28 @@ void BKE_mesh_from_metaball(ListBase *lb, Mesh *me) /** * Specialized function to use when we _know_ existing edges don't overlap with poly edges. */ -static void make_edges_mdata_extend( - MEdge **r_alledge, int *r_totedge, const MPoly *mpoly, MLoop *mloop, const int totpoly) +static void make_edges_mdata_extend(Mesh &mesh) { - int totedge = *r_totedge; - int totedge_new; - EdgeHash *eh; - uint eh_reserve; + int totedge = mesh.totedge; const MPoly *mp; int i; - eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(totpoly)); - eh = BLI_edgehash_new_ex(__func__, eh_reserve); + Span<MPoly> polys(mesh.mpoly, mesh.totpoly); + MutableSpan<MLoop> loops(mesh.mloop, mesh.totloop); - for (i = 0, mp = mpoly; i < totpoly; i++, mp++) { - BKE_mesh_poly_edgehash_insert(eh, mp, mloop + mp->loopstart); + const int eh_reserve = max_ii(totedge, BLI_EDGEHASH_SIZE_GUESS_FROM_POLYS(mesh.totpoly)); + EdgeHash *eh = BLI_edgehash_new_ex(__func__, eh_reserve); + + for (const MPoly &poly : polys) { + BKE_mesh_poly_edgehash_insert(eh, &poly, &loops[poly.loopstart]); } - totedge_new = BLI_edgehash_len(eh); + const int totedge_new = BLI_edgehash_len(eh); #ifdef DEBUG /* ensure that there's no overlap! */ if (totedge_new) { - MEdge *medge = *r_alledge; + MEdge *medge = mesh.medge; for (i = 0; i < totedge; i++, medge++) { BLI_assert(BLI_edgehash_haskey(eh, medge->v1, medge->v2) == false); } @@ -157,19 +159,15 @@ static void make_edges_mdata_extend( #endif if (totedge_new) { - EdgeHashIterator *ehi; - MEdge *medge; - uint e_index = totedge; + CustomData_realloc(&mesh.edata, totedge + totedge_new); + BKE_mesh_update_customdata_pointers(&mesh, false); - *r_alledge = medge = (MEdge *)(*r_alledge ? - MEM_reallocN(*r_alledge, - sizeof(MEdge) * (totedge + totedge_new)) : - MEM_calloc_arrayN(totedge_new, sizeof(MEdge), __func__)); - medge += totedge; + MEdge *medge = mesh.medge + totedge; - totedge += totedge_new; + mesh.totedge += totedge_new; - /* --- */ + EdgeHashIterator *ehi; + uint e_index = totedge; for (ehi = BLI_edgehashIterator_new(eh); BLI_edgehashIterator_isDone(ehi) == false; BLI_edgehashIterator_step(ehi), ++medge, e_index++) { BLI_edgehashIterator_getKey(ehi, &medge->v1, &medge->v2); @@ -180,10 +178,8 @@ static void make_edges_mdata_extend( } BLI_edgehashIterator_free(ehi); - *r_totedge = totedge; - - for (i = 0, mp = mpoly; i < totpoly; i++, mp++) { - MLoop *l = &mloop[mp->loopstart]; + for (i = 0, mp = mesh.mpoly; i < mesh.totpoly; i++, mp++) { + MLoop *l = &loops[mp->loopstart]; MLoop *l_prev = (l + (mp->totloop - 1)); int j; for (j = 0; j < mp->totloop; j++, l++) { @@ -197,25 +193,8 @@ static void make_edges_mdata_extend( BLI_edgehash_free(eh, nullptr); } -/* Initialize mverts, medges and, faces for converting nurbs to mesh and derived mesh */ -/* use specified dispbase */ -static int mesh_nurbs_displist_to_mdata(const Curve *cu, - const ListBase *dispbase, - MVert **r_allvert, - int *r_totvert, - MEdge **r_alledge, - int *r_totedge, - MLoop **r_allloop, - MPoly **r_allpoly, - MLoopUV **r_alluv, - int *r_totloop, - int *r_totpoly) +static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispbase) { - MVert *mvert; - MPoly *mpoly; - MLoop *mloop; - MLoopUV *mloopuv = nullptr; - MEdge *medge; const float *data; int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0; int p1, p2, p3, p4, *index; @@ -257,21 +236,21 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, } if (totvert == 0) { - /* Make Sure you check ob->data is a curve. */ - // error("can't convert"); - return -1; + return BKE_mesh_new_nomain(0, 0, 0, 0, 0); } - *r_allvert = mvert = (MVert *)MEM_calloc_arrayN(totvert, sizeof(MVert), "nurbs_init mvert"); - *r_alledge = medge = (MEdge *)MEM_calloc_arrayN(totedge, sizeof(MEdge), "nurbs_init medge"); - *r_allloop = mloop = (MLoop *)MEM_calloc_arrayN( - totpoly, sizeof(MLoop[4]), "nurbs_init mloop"); /* totloop */ - *r_allpoly = mpoly = (MPoly *)MEM_calloc_arrayN(totpoly, sizeof(MPoly), "nurbs_init mloop"); + Mesh *mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly); + MutableSpan<MVert> verts(mesh->mvert, mesh->totvert); + MutableSpan<MEdge> edges(mesh->medge, mesh->totedge); + MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly); + MutableSpan<MLoop> loops(mesh->mloop, mesh->totloop); - if (r_alluv) { - *r_alluv = mloopuv = (MLoopUV *)MEM_calloc_arrayN( - totpoly, sizeof(MLoopUV[4]), "nurbs_init mloopuv"); - } + MVert *mvert = verts.data(); + MEdge *medge = edges.data(); + MPoly *mpoly = polys.data(); + MLoop *mloop = loops.data(); + MLoopUV *mloopuv = static_cast<MLoopUV *>(CustomData_add_layer_named( + &mesh->ldata, CD_MLOOPUV, CD_CALLOC, nullptr, mesh->totloop, "UVMap")); /* verts and faces */ vertcount = 0; @@ -346,7 +325,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, mloop[0].v = startvert + index[0]; mloop[1].v = startvert + index[2]; mloop[2].v = startvert + index[1]; - mpoly->loopstart = (int)(mloop - (*r_allloop)); + mpoly->loopstart = (int)(mloop - loops.data()); mpoly->totloop = 3; mpoly->mat_nr = dl->col; @@ -406,7 +385,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, mloop[1].v = p3; mloop[2].v = p4; mloop[3].v = p2; - mpoly->loopstart = (int)(mloop - (*r_allloop)); + mpoly->loopstart = (int)(mloop - loops.data()); mpoly->totloop = 4; mpoly->mat_nr = dl->col; @@ -458,15 +437,10 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, } if (totpoly) { - make_edges_mdata_extend(r_alledge, &totedge, *r_allpoly, *r_allloop, totpoly); + make_edges_mdata_extend(*mesh); } - *r_totpoly = totpoly; - *r_totloop = totloop; - *r_totedge = totedge; - *r_totvert = totvert; - - return 0; + return mesh; } /** @@ -487,60 +461,12 @@ static void mesh_copy_texture_space_from_curve_type(const Curve *cu, Mesh *me) Mesh *BKE_mesh_new_nomain_from_curve_displist(const Object *ob, const ListBase *dispbase) { const Curve *cu = (const Curve *)ob->data; - Mesh *mesh; - MVert *allvert; - MEdge *alledge; - MLoop *allloop; - MPoly *allpoly; - MLoopUV *alluv = nullptr; - int totvert, totedge, totloop, totpoly; - - if (mesh_nurbs_displist_to_mdata(cu, - dispbase, - &allvert, - &totvert, - &alledge, - &totedge, - &allloop, - &allpoly, - &alluv, - &totloop, - &totpoly) != 0) { - /* Error initializing mdata. This often happens when curve is empty */ - return BKE_mesh_new_nomain(0, 0, 0, 0, 0); - } - - mesh = BKE_mesh_new_nomain(totvert, totedge, 0, totloop, totpoly); - - if (totvert != 0) { - memcpy(mesh->mvert, allvert, totvert * sizeof(MVert)); - } - if (totedge != 0) { - memcpy(mesh->medge, alledge, totedge * sizeof(MEdge)); - } - if (totloop != 0) { - memcpy(mesh->mloop, allloop, totloop * sizeof(MLoop)); - } - if (totpoly != 0) { - memcpy(mesh->mpoly, allpoly, totpoly * sizeof(MPoly)); - } - - if (alluv) { - const char *uvname = "UVMap"; - CustomData_add_layer_named(&mesh->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, totloop, uvname); - } + Mesh *mesh = mesh_nurbs_displist_to_mesh(cu, dispbase); mesh_copy_texture_space_from_curve_type(cu, mesh); - - /* Copy curve materials. */ mesh->mat = (Material **)MEM_dupallocN(cu->mat); mesh->totcol = cu->totcol; - MEM_freeN(allvert); - MEM_freeN(alledge); - MEM_freeN(allloop); - MEM_freeN(allpoly); - return mesh; } diff --git a/source/blender/blenkernel/intern/mesh_evaluate.cc b/source/blender/blenkernel/intern/mesh_evaluate.cc index 7d26262a504..9dba8eab340 100644 --- a/source/blender/blenkernel/intern/mesh_evaluate.cc +++ b/source/blender/blenkernel/intern/mesh_evaluate.cc @@ -22,15 +22,17 @@ #include "BLI_math.h" #include "BLI_span.hh" #include "BLI_utildefines.h" +#include "BLI_virtual_array.hh" #include "BKE_customdata.h" +#include "BKE_attribute.hh" #include "BKE_mesh.h" #include "BKE_multires.h" -using blender::IndexRange; using blender::MutableSpan; using blender::Span; +using blender::VArray; /* -------------------------------------------------------------------- */ /** \name Polygon Calculations @@ -732,75 +734,90 @@ void BKE_mesh_polygons_flip(MPoly *mpoly, MLoop *mloop, CustomData *ldata, int t /** \name Mesh Flag Flushing * \{ */ -void BKE_mesh_flush_hidden_from_verts_ex(const MVert *mvert, - const MLoop *mloop, - MEdge *medge, - const int totedge, - MPoly *mpoly, - const int totpoly) +void BKE_mesh_flush_hidden_from_verts(Mesh *me) { - int i, j; + using namespace blender; + using namespace blender::bke; + MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); - for (i = 0; i < totedge; i++) { - MEdge *e = &medge[i]; - if (mvert[e->v1].flag & ME_HIDE || mvert[e->v2].flag & ME_HIDE) { - e->flag |= ME_HIDE; - } - else { - e->flag &= ~ME_HIDE; - } + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + if (hide_vert.is_single() && !hide_vert.get_internal_single()) { + attributes.remove(".hide_edge"); + attributes.remove(".hide_poly"); + return; } - for (i = 0; i < totpoly; i++) { - MPoly *p = &mpoly[i]; - p->flag &= (char)~ME_HIDE; - for (j = 0; j < p->totloop; j++) { - if (mvert[mloop[p->loopstart + j].v].flag & ME_HIDE) { - p->flag |= ME_HIDE; - } - } + const VArraySpan<bool> hide_vert_span{hide_vert}; + const Span<MEdge> edges(me->medge, me->totedge); + const Span<MPoly> polys(me->mpoly, me->totpoly); + const Span<MLoop> loops(me->mloop, me->totloop); + + /* Hide edges when either of their vertices are hidden. */ + SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_edge", ATTR_DOMAIN_EDGE); + for (const int i : edges.index_range()) { + const MEdge &edge = edges[i]; + hide_edge.span[i] = hide_vert_span[edge.v1] || hide_vert_span[edge.v2]; } -} -void BKE_mesh_flush_hidden_from_verts(Mesh *me) -{ - BKE_mesh_flush_hidden_from_verts_ex( - me->mvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly); + hide_edge.finish(); + + /* Hide polygons when any of their vertices are hidden. */ + SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_poly", ATTR_DOMAIN_FACE); + for (const int i : polys.index_range()) { + const MPoly &poly = polys[i]; + const Span<MLoop> poly_loops = loops.slice(poly.loopstart, poly.totloop); + hide_poly.span[i] = std::any_of(poly_loops.begin(), poly_loops.end(), [&](const MLoop &loop) { + return hide_vert_span[loop.v]; + }); + } + hide_poly.finish(); } -void BKE_mesh_flush_hidden_from_polys_ex(MVert *mvert, - const MLoop *mloop, - MEdge *medge, - const int UNUSED(totedge), - const MPoly *mpoly, - const int totpoly) +void BKE_mesh_flush_hidden_from_polys(Mesh *me) { - int i = totpoly; - for (const MPoly *mp = mpoly; i--; mp++) { - if (mp->flag & ME_HIDE) { - const MLoop *ml; - int j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - mvert[ml->v].flag |= ME_HIDE; - medge[ml->e].flag |= ME_HIDE; + using namespace blender; + using namespace blender::bke; + MutableAttributeAccessor attributes = mesh_attributes_for_write(*me); + + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (hide_poly.is_single() && !hide_poly.get_internal_single()) { + attributes.remove(".hide_vert"); + attributes.remove(".hide_edge"); + return; + } + const VArraySpan<bool> hide_poly_span{hide_poly}; + const Span<MPoly> polys(me->mpoly, me->totpoly); + const Span<MLoop> loops(me->mloop, me->totloop); + SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_vert", ATTR_DOMAIN_POINT); + SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_edge", ATTR_DOMAIN_EDGE); + + /* Hide all edges or vertices connected to hidden polygons. */ + for (const int i : polys.index_range()) { + if (hide_poly_span[i]) { + const MPoly &poly = polys[i]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + hide_vert.span[loop.v] = true; + hide_edge.span[loop.e] = true; } } } - - i = totpoly; - for (const MPoly *mp = mpoly; i--; mp++) { - if ((mp->flag & ME_HIDE) == 0) { - const MLoop *ml; - int j = mp->totloop; - for (ml = &mloop[mp->loopstart]; j--; ml++) { - mvert[ml->v].flag &= (char)~ME_HIDE; - medge[ml->e].flag &= (short)~ME_HIDE; + /* Unhide vertices and edges connected to visible polygons. */ + for (const int i : polys.index_range()) { + if (!hide_poly_span[i]) { + const MPoly &poly = polys[i]; + for (const MLoop &loop : loops.slice(poly.loopstart, poly.totloop)) { + hide_vert.span[loop.v] = false; + hide_edge.span[loop.e] = false; } } } -} -void BKE_mesh_flush_hidden_from_polys(Mesh *me) -{ - BKE_mesh_flush_hidden_from_polys_ex( - me->mvert, me->mloop, me->medge, me->totedge, me->mpoly, me->totpoly); + + hide_vert.finish(); + hide_edge.finish(); } void BKE_mesh_flush_select_from_polys_ex(MVert *mvert, @@ -848,11 +865,13 @@ void BKE_mesh_flush_select_from_polys(Mesh *me) static void mesh_flush_select_from_verts(const Span<MVert> verts, const Span<MLoop> loops, + const VArray<bool> &hide_edge, + const VArray<bool> &hide_poly, MutableSpan<MEdge> edges, MutableSpan<MPoly> polys) { for (const int i : edges.index_range()) { - if ((edges[i].flag & ME_HIDE) == 0) { + if (!hide_edge[i]) { MEdge &edge = edges[i]; if ((verts[edge.v1].flag & SELECT) && (verts[edge.v2].flag & SELECT)) { edge.flag |= SELECT; @@ -864,7 +883,7 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts, } for (const int i : polys.index_range()) { - if (polys[i].flag & ME_HIDE) { + if (hide_poly[i]) { continue; } MPoly &poly = polys[i]; @@ -885,10 +904,14 @@ static void mesh_flush_select_from_verts(const Span<MVert> verts, void BKE_mesh_flush_select_from_verts(Mesh *me) { - mesh_flush_select_from_verts({me->mvert, me->totvert}, - {me->mloop, me->totloop}, - {me->medge, me->totedge}, - {me->mpoly, me->totpoly}); + const blender::bke::AttributeAccessor attributes = blender::bke::mesh_attributes(*me); + mesh_flush_select_from_verts( + {me->mvert, me->totvert}, + {me->mloop, me->totloop}, + attributes.lookup_or_default<bool>(".hide_edge", ATTR_DOMAIN_EDGE, false), + attributes.lookup_or_default<bool>(".hide_poly", ATTR_DOMAIN_FACE, false), + {me->medge, me->totedge}, + {me->mpoly, me->totpoly}); } /** \} */ diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 479dd6a012a..cc96a5e8c60 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -7,7 +7,7 @@ * Functions to convert mesh data to and from legacy formats like #MFace. */ -// #include <climits> +#define DNA_DEPRECATED_ALLOW #include "MEM_guardedalloc.h" @@ -18,8 +18,10 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_polyfill_2d.h" +#include "BLI_task.hh" #include "BLI_utildefines.h" +#include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_mesh_legacy_convert.h" @@ -874,3 +876,89 @@ void BKE_mesh_add_mface_layers(CustomData *fdata, CustomData *ldata, int total) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Hide Attribute and Legacy Flag Conversion + * \{ */ + +void BKE_mesh_legacy_convert_hide_layers_to_flags(Mesh *mesh) +{ + using namespace blender; + using namespace blender::bke; + const AttributeAccessor attributes = mesh_attributes(*mesh); + + MutableSpan<MVert> verts(mesh->mvert, mesh->totvert); + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(verts[i].flag, hide_vert[i], ME_HIDE); + } + }); + + MutableSpan<MEdge> edges(mesh->medge, mesh->totedge); + const VArray<bool> hide_edge = attributes.lookup_or_default<bool>( + ".hide_edge", ATTR_DOMAIN_EDGE, false); + threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(edges[i].flag, hide_edge[i], ME_HIDE); + } + }); + + MutableSpan<MPoly> polys(mesh->mpoly, mesh->totpoly); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + SET_FLAG_FROM_TEST(polys[i].flag, hide_poly[i], ME_HIDE); + } + }); +} + +void BKE_mesh_legacy_convert_flags_to_hide_layers(Mesh *mesh) +{ + using namespace blender; + using namespace blender::bke; + MutableAttributeAccessor attributes = mesh_attributes_for_write(*mesh); + + const Span<MVert> verts(mesh->mvert, mesh->totvert); + if (std::any_of( + verts.begin(), verts.end(), [](const MVert &vert) { return vert.flag & ME_HIDE; })) { + SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_vert", ATTR_DOMAIN_POINT); + threading::parallel_for(verts.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + hide_vert.span[i] = verts[i].flag & ME_HIDE; + } + }); + hide_vert.finish(); + } + + const Span<MEdge> edges(mesh->medge, mesh->totedge); + if (std::any_of( + edges.begin(), edges.end(), [](const MEdge &edge) { return edge.flag & ME_HIDE; })) { + SpanAttributeWriter<bool> hide_edge = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_edge", ATTR_DOMAIN_EDGE); + threading::parallel_for(edges.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + hide_edge.span[i] = edges[i].flag & ME_HIDE; + } + }); + hide_edge.finish(); + } + + const Span<MPoly> polys(mesh->mpoly, mesh->totpoly); + if (std::any_of( + polys.begin(), polys.end(), [](const MPoly &poly) { return poly.flag & ME_HIDE; })) { + SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_only_span<bool>( + ".hide_poly", ATTR_DOMAIN_FACE); + threading::parallel_for(polys.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + hide_poly.span[i] = polys[i].flag & ME_HIDE; + } + }); + hide_poly.finish(); + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_mapping.c b/source/blender/blenkernel/intern/mesh_mapping.c index 9c4098e2db6..798fe087ea8 100644 --- a/source/blender/blenkernel/intern/mesh_mapping.c +++ b/source/blender/blenkernel/intern/mesh_mapping.c @@ -29,6 +29,7 @@ /* ngon version wip, based on BM_uv_vert_map_create */ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, + const bool *hide_poly, const MLoop *mloop, const MLoopUV *mloopuv, uint totpoly, @@ -51,7 +52,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, /* generate UvMapVert array */ mp = mpoly; for (a = 0; a < totpoly; a++, mp++) { - if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) { + if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) { totuv += mp->totloop; } } @@ -74,7 +75,7 @@ UvVertMap *BKE_mesh_uv_vert_map_create(const MPoly *mpoly, mp = mpoly; for (a = 0; a < totpoly; a++, mp++) { - if (!selected || (!(mp->flag & ME_HIDE) && (mp->flag & ME_FACE_SEL))) { + if (!selected || (!(hide_poly && hide_poly[a]) && (mp->flag & ME_FACE_SEL))) { float(*tf_uv)[2] = NULL; if (use_winding) { diff --git a/source/blender/blenkernel/intern/mesh_remap.c b/source/blender/blenkernel/intern/mesh_remap.c index 3a37c29c1d0..5313cc39646 100644 --- a/source/blender/blenkernel/intern/mesh_remap.c +++ b/source/blender/blenkernel/intern/mesh_remap.c @@ -312,15 +312,10 @@ void BKE_mesh_remap_calc_source_cddata_masks_from_map_modes(const int UNUSED(ver { /* vert, edge and poly mapping modes never need extra cddata from source object. */ const bool need_lnors_src = (loop_mode & MREMAP_USE_LOOP) && (loop_mode & MREMAP_USE_NORMAL); - const bool need_pnors_src = need_lnors_src || - ((loop_mode & MREMAP_USE_POLY) && (loop_mode & MREMAP_USE_NORMAL)); if (need_lnors_src) { r_cddata_mask->lmask |= CD_MASK_NORMAL; } - if (need_pnors_src) { - r_cddata_mask->pmask |= CD_MASK_NORMAL; - } } void BKE_mesh_remap_init(MeshPairRemap *map, const int items_num) diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index dd09a3d6917..e54f2e6d687 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -16,9 +16,9 @@ template<typename T> BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, const Span<int> looptri_indices, const Span<float3> bary_coords, - const VArray<T> &data_in, + const VArray<T> &src, const IndexMask mask, - const MutableSpan<T> data_out) + const MutableSpan<T> dst) { const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; @@ -32,30 +32,30 @@ BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, const int v1_index = mesh.mloop[looptri.tri[1]].v; const int v2_index = mesh.mloop[looptri.tri[2]].v; - const T v0 = data_in[v0_index]; - const T v1 = data_in[v1_index]; - const T v2 = data_in[v2_index]; + const T v0 = src[v0_index]; + const T v1 = src[v1_index]; + const T v2 = src[v2_index]; const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); - data_out[i] = interpolated_value; + dst[i] = interpolated_value; } } void sample_point_attribute(const Mesh &mesh, const Span<int> looptri_indices, const Span<float3> bary_coords, - const GVArray &data_in, + const GVArray &src, const IndexMask mask, - const GMutableSpan data_out) + const GMutableSpan dst) { - BLI_assert(data_in.size() == mesh.totvert); - BLI_assert(data_in.type() == data_out.type()); + BLI_assert(src.size() == mesh.totvert); + BLI_assert(src.type() == dst.type()); - const CPPType &type = data_in.type(); + const CPPType &type = src.type(); attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); sample_point_attribute<T>( - mesh, looptri_indices, bary_coords, data_in.typed<T>(), mask, data_out.typed<T>()); + mesh, looptri_indices, bary_coords, src.typed<T>(), mask, dst.typed<T>()); }); } @@ -63,9 +63,9 @@ template<typename T> BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, const Span<int> looptri_indices, const Span<float3> bary_coords, - const VArray<T> &data_in, + const VArray<T> &src, const IndexMask mask, - const MutableSpan<T> data_out) + const MutableSpan<T> dst) { const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; @@ -79,39 +79,39 @@ BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, const int loop_index_1 = looptri.tri[1]; const int loop_index_2 = looptri.tri[2]; - const T v0 = data_in[loop_index_0]; - const T v1 = data_in[loop_index_1]; - const T v2 = data_in[loop_index_2]; + const T v0 = src[loop_index_0]; + const T v1 = src[loop_index_1]; + const T v2 = src[loop_index_2]; const T interpolated_value = attribute_math::mix3(bary_coord, v0, v1, v2); - data_out[i] = interpolated_value; + dst[i] = interpolated_value; } } void sample_corner_attribute(const Mesh &mesh, const Span<int> looptri_indices, const Span<float3> bary_coords, - const GVArray &data_in, + const GVArray &src, const IndexMask mask, - const GMutableSpan data_out) + const GMutableSpan dst) { - BLI_assert(data_in.size() == mesh.totloop); - BLI_assert(data_in.type() == data_out.type()); + BLI_assert(src.size() == mesh.totloop); + BLI_assert(src.type() == dst.type()); - const CPPType &type = data_in.type(); + const CPPType &type = src.type(); attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); sample_corner_attribute<T>( - mesh, looptri_indices, bary_coords, data_in.typed<T>(), mask, data_out.typed<T>()); + mesh, looptri_indices, bary_coords, src.typed<T>(), mask, dst.typed<T>()); }); } template<typename T> void sample_face_attribute(const Mesh &mesh, const Span<int> looptri_indices, - const VArray<T> &data_in, + const VArray<T> &src, const IndexMask mask, - const MutableSpan<T> data_out) + const MutableSpan<T> dst) { const Span<MLoopTri> looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; @@ -120,23 +120,23 @@ void sample_face_attribute(const Mesh &mesh, const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const int poly_index = looptri.poly; - data_out[i] = data_in[poly_index]; + dst[i] = src[poly_index]; } } void sample_face_attribute(const Mesh &mesh, const Span<int> looptri_indices, - const GVArray &data_in, + const GVArray &src, const IndexMask mask, - const GMutableSpan data_out) + const GMutableSpan dst) { - BLI_assert(data_in.size() == mesh.totpoly); - BLI_assert(data_in.type() == data_out.type()); + BLI_assert(src.size() == mesh.totpoly); + BLI_assert(src.type() == dst.type()); - const CPPType &type = data_in.type(); + const CPPType &type = src.type(); attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); - sample_face_attribute<T>(mesh, looptri_indices, data_in.typed<T>(), mask, data_out.typed<T>()); + sample_face_attribute<T>(mesh, looptri_indices, src.typed<T>(), mask, dst.typed<T>()); }); } @@ -219,45 +219,31 @@ void MeshAttributeInterpolator::sample_data(const GVArray &src, if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { switch (mode) { case eAttributeMapMode::INTERPOLATED: - weights = ensure_barycentric_coords(); + weights = this->ensure_barycentric_coords(); break; case eAttributeMapMode::NEAREST: - weights = ensure_nearest_weights(); + weights = this->ensure_nearest_weights(); break; } } /* Interpolate the source attributes on the surface. */ switch (domain) { - case ATTR_DOMAIN_POINT: { + case ATTR_DOMAIN_POINT: sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst); break; - } - case ATTR_DOMAIN_FACE: { + case ATTR_DOMAIN_FACE: sample_face_attribute(*mesh_, looptri_indices_, src, mask_, dst); break; - } - case ATTR_DOMAIN_CORNER: { + case ATTR_DOMAIN_CORNER: sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst); break; - } - case ATTR_DOMAIN_EDGE: { + case ATTR_DOMAIN_EDGE: /* Not yet supported. */ break; - } - default: { + default: BLI_assert_unreachable(); break; - } - } -} - -void MeshAttributeInterpolator::sample_attribute(const GAttributeReader &src_attribute, - GSpanAttributeWriter &dst_attribute, - eAttributeMapMode mode) -{ - if (src_attribute && dst_attribute) { - this->sample_data(src_attribute.varray, src_attribute.domain, mode, dst_attribute.span); } } diff --git a/source/blender/blenkernel/intern/mesh_tangent.c b/source/blender/blenkernel/intern/mesh_tangent.c index a677a0d6ebb..1772419e1f3 100644 --- a/source/blender/blenkernel/intern/mesh_tangent.c +++ b/source/blender/blenkernel/intern/mesh_tangent.c @@ -716,7 +716,7 @@ void BKE_mesh_calc_loop_tangents(Mesh *me_eval, { BKE_mesh_runtime_looptri_ensure(me_eval); - /* TODO(campbell): store in Mesh.runtime to avoid recalculation. */ + /* TODO(@campbellbarton): store in Mesh.runtime to avoid recalculation. */ short tangent_mask = 0; BKE_mesh_calc_loop_tangent_ex(me_eval->mvert, me_eval->mpoly, diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index 9b2697ecc84..5bcbdb399e4 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -1001,10 +1001,6 @@ bool BKE_mesh_validate_all_customdata(CustomData *vdata, CustomData_MeshMasks mask = {0}; if (check_meshmask) { mask = CD_MASK_MESH; - /* Normal data isn't in the mask since it is derived data, - * but it is valid and should not be removed. */ - mask.vmask |= CD_MASK_NORMAL; - mask.pmask |= CD_MASK_NORMAL; } is_valid &= mesh_validate_customdata( diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index 01eb4970f7e..60d6b51594a 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -142,7 +142,8 @@ static ModifierData *modifier_allocate_and_init(int type) md->type = type; md->mode = eModifierMode_Realtime | eModifierMode_Render; md->flag = eModifierFlag_OverrideLibrary_Local; - md->ui_expand_flag = 1; /* Only open the main panel at the beginning, not the sub-panels. */ + /* Only open the main panel at the beginning, not the sub-panels. */ + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; if (mti->flags & eModifierTypeFlag_EnableInEditmode) { md->mode |= eModifierMode_Editmode; diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 407a2c8955c..cc3a8b5bb0e 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -29,6 +29,7 @@ #include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_vfont_types.h" +#include "DNA_volume_types.h" #include "BKE_collection.h" #include "BKE_duplilist.h" @@ -164,10 +165,8 @@ static bool copy_dupli_context( * * \param mat: is transform of the object relative to current context (including #Object.obmat). */ -static DupliObject *make_dupli(const DupliContext *ctx, - Object *ob, - const float mat[4][4], - int index) +static DupliObject *make_dupli( + const DupliContext *ctx, Object *ob, const ID *object_data, const float mat[4][4], int index) { DupliObject *dob; int i; @@ -182,7 +181,7 @@ static DupliObject *make_dupli(const DupliContext *ctx, } dob->ob = ob; - dob->ob_data = (ID *)ob->data; + dob->ob_data = const_cast<ID *>(object_data); mul_m4_m4m4(dob->mat, (float(*)[4])ctx->space_mat, mat); dob->type = ctx->gen->type; @@ -226,6 +225,14 @@ static DupliObject *make_dupli(const DupliContext *ctx, return dob; } +static DupliObject *make_dupli(const DupliContext *ctx, + Object *ob, + const float mat[4][4], + int index) +{ + return make_dupli(ctx, ob, static_cast<ID *>(ob->data), mat, index); +} + /** * Recursive dupli-objects. * @@ -777,28 +784,24 @@ static void make_duplis_geometry_set_impl(const DupliContext *ctx, int component_index = 0; if (ctx->object->type != OB_MESH || geometry_set_is_instance) { if (const Mesh *mesh = geometry_set.get_mesh_for_read()) { - DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); - dupli->ob_data = (ID *)mesh; + make_dupli(ctx, ctx->object, &mesh->id, parent_transform, component_index++); } } if (ctx->object->type != OB_VOLUME || geometry_set_is_instance) { if (const Volume *volume = geometry_set.get_volume_for_read()) { - DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); - dupli->ob_data = (ID *)volume; + make_dupli(ctx, ctx->object, &volume->id, parent_transform, component_index++); } } if (!ELEM(ctx->object->type, OB_CURVES_LEGACY, OB_FONT, OB_CURVES) || geometry_set_is_instance) { if (const CurveComponent *component = geometry_set.get_component_for_read<CurveComponent>()) { if (const Curve *curve = component->get_curve_for_render()) { - DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); - dupli->ob_data = (ID *)curve; + make_dupli(ctx, ctx->object, &curve->id, parent_transform, component_index++); } } } if (ctx->object->type != OB_POINTCLOUD || geometry_set_is_instance) { if (const PointCloud *pointcloud = geometry_set.get_pointcloud_for_read()) { - DupliObject *dupli = make_dupli(ctx, ctx->object, parent_transform, component_index++); - dupli->ob_data = (ID *)pointcloud; + make_dupli(ctx, ctx->object, &pointcloud->id, parent_transform, component_index++); } } const bool creates_duplis_for_components = component_index >= 1; diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index 8ff02c7e698..99c4d92d284 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -149,10 +149,6 @@ void BKE_object_handle_data_update(Depsgraph *depsgraph, Scene *scene, Object *o cddata_masks.pmask |= CD_MASK_PROP_ALL; cddata_masks.lmask |= CD_MASK_PROP_ALL; - /* Also copy over normal layers to avoid recomputation. */ - cddata_masks.pmask |= CD_MASK_NORMAL; - cddata_masks.vmask |= CD_MASK_NORMAL; - /* Make sure Freestyle edge/face marks appear in DM for render (see T40315). * Due to Line Art implementation, edge marks should also be shown in viewport. */ #ifdef WITH_FREESTYLE @@ -285,45 +281,39 @@ void BKE_object_eval_uber_transform(Depsgraph *UNUSED(depsgraph), Object *UNUSED { } -void BKE_object_data_batch_cache_dirty_tag(ID *object_data) +void BKE_object_batch_cache_dirty_tag(Object *ob) { - switch (GS(object_data->name)) { - case ID_ME: - BKE_mesh_batch_cache_dirty_tag((struct Mesh *)object_data, BKE_MESH_BATCH_DIRTY_ALL); + switch (ob->type) { + case OB_MESH: + BKE_mesh_batch_cache_dirty_tag((struct Mesh *)ob->data, BKE_MESH_BATCH_DIRTY_ALL); break; - case ID_LT: - BKE_lattice_batch_cache_dirty_tag((struct Lattice *)object_data, - BKE_LATTICE_BATCH_DIRTY_ALL); + case OB_LATTICE: + BKE_lattice_batch_cache_dirty_tag((struct Lattice *)ob->data, BKE_LATTICE_BATCH_DIRTY_ALL); break; - case ID_CU_LEGACY: - BKE_curve_batch_cache_dirty_tag((struct Curve *)object_data, BKE_CURVE_BATCH_DIRTY_ALL); + case OB_CURVES_LEGACY: + BKE_curve_batch_cache_dirty_tag((struct Curve *)ob->data, BKE_CURVE_BATCH_DIRTY_ALL); break; - case ID_MB: - BKE_mball_batch_cache_dirty_tag((struct MetaBall *)object_data, BKE_MBALL_BATCH_DIRTY_ALL); + case OB_MBALL: + BKE_mball_batch_cache_dirty_tag((struct MetaBall *)ob->data, BKE_MBALL_BATCH_DIRTY_ALL); break; - case ID_GD: - BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)object_data); + case OB_GPENCIL: + BKE_gpencil_batch_cache_dirty_tag((struct bGPdata *)ob->data); break; - case ID_CV: - BKE_curves_batch_cache_dirty_tag((struct Curves *)object_data, BKE_CURVES_BATCH_DIRTY_ALL); + case OB_CURVES: + BKE_curves_batch_cache_dirty_tag((struct Curves *)ob->data, BKE_CURVES_BATCH_DIRTY_ALL); break; - case ID_PT: - BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)object_data, + case OB_POINTCLOUD: + BKE_pointcloud_batch_cache_dirty_tag((struct PointCloud *)ob->data, BKE_POINTCLOUD_BATCH_DIRTY_ALL); break; - case ID_VO: - BKE_volume_batch_cache_dirty_tag((struct Volume *)object_data, BKE_VOLUME_BATCH_DIRTY_ALL); + case OB_VOLUME: + BKE_volume_batch_cache_dirty_tag((struct Volume *)ob->data, BKE_VOLUME_BATCH_DIRTY_ALL); break; default: break; } } -void BKE_object_batch_cache_dirty_tag(Object *ob) -{ - BKE_object_data_batch_cache_dirty_tag(ob->data); -} - void BKE_object_eval_uber_data(Depsgraph *depsgraph, Scene *scene, Object *ob) { DEG_debug_print_eval(depsgraph, __func__, ob->id.name, ob); diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index dec9a594938..cd1f24fee37 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -1385,9 +1385,8 @@ void BKE_ocean_bake(struct Ocean *o, void (*update_cb)(void *, float progress, int *cancel), void *update_cb_data) { - /* NOTE(campbell): some of these values remain uninitialized unless certain options - * are enabled, take care that BKE_ocean_eval_ij() initializes a member - * before use. */ + /* NOTE(@campbellbarton): some of these values remain uninitialized unless certain options + * are enabled, take care that #BKE_ocean_eval_ij() initializes a member before use. */ OceanResult ocr; ImageFormatData imf = {0}; @@ -1441,7 +1440,7 @@ void BKE_ocean_bake(struct Ocean *o, rgb_to_rgba_unit_alpha(&ibuf_disp->rect_float[4 * (res_x * y + x)], ocr.disp); if (o->_do_jacobian) { - /* TODO(campbell): cleanup unused code. */ + /* TODO(@campbellbarton): cleanup unused code. */ float /* r, */ /* UNUSED */ pr = 0.0f, foam_result; float neg_disp, neg_eplus; diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index 9b0d15ac702..922ea45703d 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -1243,11 +1243,13 @@ void BKE_paint_blend_read_lib(BlendLibReader *reader, Scene *sce, Paint *p) } } -bool paint_is_face_hidden(const MLoopTri *lt, const MVert *mvert, const MLoop *mloop) +bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_vert, const MLoop *mloop) { - return ((mvert[mloop[lt->tri[0]].v].flag & ME_HIDE) || - (mvert[mloop[lt->tri[1]].v].flag & ME_HIDE) || - (mvert[mloop[lt->tri[2]].v].flag & ME_HIDE)); + if (!hide_vert) { + return false; + } + return ((hide_vert[mloop[lt->tri[0]].v]) || (hide_vert[mloop[lt->tri[1]].v]) || + (hide_vert[mloop[lt->tri[2]].v])); } bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y) @@ -1433,10 +1435,10 @@ static void sculptsession_free_pbvh(Object *object) MEM_SAFE_FREE(ss->persistent_base); - MEM_SAFE_FREE(ss->preview_vert_index_list); - ss->preview_vert_index_count = 0; + MEM_SAFE_FREE(ss->preview_vert_list); + ss->preview_vert_count = 0; - MEM_SAFE_FREE(ss->preview_vert_index_list); + MEM_SAFE_FREE(ss->preview_vert_list); MEM_SAFE_FREE(ss->vertex_info.connected_component); MEM_SAFE_FREE(ss->vertex_info.boundary); @@ -2068,9 +2070,11 @@ void BKE_sculpt_face_sets_ensure_from_base_mesh_visibility(Mesh *mesh) } int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS); + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly"); for (int i = 0; i < mesh->totpoly; i++) { - if (!(mesh->mpoly[i].flag & ME_HIDE)) { + if (!(hide_poly && hide_poly[i])) { continue; } @@ -2095,9 +2099,13 @@ void BKE_sculpt_sync_face_sets_visibility_to_base_mesh(Mesh *mesh) return; } + bool *hide_poly = (bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"); + if (!hide_poly) { + return; + } + for (int i = 0; i < mesh->totpoly; i++) { - const bool is_face_set_visible = face_sets[i] >= 0; - SET_FLAG_FROM_TEST(mesh->mpoly[i].flag, !is_face_set_visible, ME_HIDE); + hide_poly[i] = face_sets[i] < 0; } BKE_mesh_flush_hidden_from_polys(mesh); diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 2471d3baa59..85a8d6c817f 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -5413,8 +5413,8 @@ void BKE_particle_system_blend_read_lib(BlendLibReader *reader, BLO_read_id_address(reader, id->lib, &psys->target_ob); if (psys->clmd) { - /* XXX(campbell): from reading existing code this seems correct but intended usage of - * pointcache /w cloth should be added in 'ParticleSystem'. */ + /* XXX(@campbellbarton): from reading existing code this seems correct but intended usage + * of pointcache /w cloth should be added in 'ParticleSystem'. */ psys->clmd->point_cache = psys->pointcache; psys->clmd->ptcaches.first = psys->clmd->ptcaches.last = NULL; BLO_read_id_address(reader, id->lib, &psys->clmd->coll_parms->group); diff --git a/source/blender/blenkernel/intern/particle_system.c b/source/blender/blenkernel/intern/particle_system.c index 4a8f029beee..e9bbcea241e 100644 --- a/source/blender/blenkernel/intern/particle_system.c +++ b/source/blender/blenkernel/intern/particle_system.c @@ -969,7 +969,7 @@ void psys_get_birth_coords( float tmat[3][3]; /* NOTE: utan_local is not taken from 'utan', we calculate from rot_vec/vtan. */ - /* NOTE(campbell): it looks like rotation phase may be applied twice + /* NOTE(@campbellbarton): it looks like rotation phase may be applied twice * (once with vtan, again below) however this isn't the case. */ float *rot_vec_local = tmat[0]; float *vtan_local = tmat[1]; diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 8c1f19f0909..4e6418942be 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -287,7 +287,7 @@ static void build_mesh_leaf_node(PBVH *pbvh, PBVHNode *node) } if (has_visible == false) { - if (!paint_is_face_hidden(lt, pbvh->verts, pbvh->mloop)) { + if (!paint_is_face_hidden(lt, pbvh->hide_vert, pbvh->mloop)) { has_visible = true; } } @@ -555,13 +555,14 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, BB cb; pbvh->mesh = mesh; - pbvh->type = PBVH_FACES; + pbvh->header.type = PBVH_FACES; pbvh->mpoly = mpoly; pbvh->mloop = mloop; pbvh->looptri = looptri; pbvh->verts = verts; BKE_mesh_vertex_normals_ensure(mesh); pbvh->vert_normals = BKE_mesh_vertex_normals_for_write(mesh); + pbvh->hide_vert = (bool *)CustomData_get_layer_named(&mesh->vdata, CD_PROP_BOOL, ".hide_vert"); pbvh->vert_bitmap = MEM_calloc_arrayN(totvert, sizeof(bool), "bvh->vert_bitmap"); pbvh->totvert = totvert; pbvh->leaf_limit = LEAF_LIMIT; @@ -615,7 +616,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh, { const int gridsize = key->grid_size; - pbvh->type = PBVH_GRIDS; + pbvh->header.type = PBVH_GRIDS; pbvh->grids = grids; pbvh->gridfaces = gridfaces; pbvh->grid_flag_mats = flagmats; @@ -1303,19 +1304,8 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, PBVH *pbvh = data->pbvh; PBVHNode *node = data->nodes[n]; - CustomData *vdata, *ldata; - - if (!pbvh->bm) { - vdata = pbvh->vdata; - ldata = pbvh->ldata; - } - else { - vdata = &pbvh->bm->vdata; - ldata = &pbvh->bm->ldata; - } - if (node->flag & PBVH_RebuildDrawBuffers) { - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_GRIDS: { bool smooth = node->totprim > 0 ? pbvh->grid_flag_mats[node->prim_indices[0]].flag & ME_SMOOTH : @@ -1326,14 +1316,11 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, } case PBVH_FACES: node->draw_buffers = GPU_pbvh_mesh_buffers_build( - pbvh->mpoly, - pbvh->mloop, + pbvh->mesh, pbvh->looptri, - pbvh->verts, - node->prim_indices, CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS), - node->totprim, - pbvh->mesh); + node->prim_indices, + node->totprim); break; case PBVH_BMESH: node->draw_buffers = GPU_pbvh_bmesh_buffers_build(pbvh->flags & @@ -1344,7 +1331,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, if (node->flag & PBVH_UpdateDrawBuffers) { const int update_flags = pbvh_get_buffers_update_flags(pbvh); - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_GRIDS: GPU_pbvh_grid_buffers_update(pbvh->vbo_id, node->draw_buffers, @@ -1360,11 +1347,12 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, update_flags); break; case PBVH_FACES: { + /* Pass vertices separately because they may be not be the same as the mesh's vertices, + * and pass normals separately because they are managed by the PBVH. */ GPU_pbvh_mesh_buffers_update(pbvh->vbo_id, node->draw_buffers, + pbvh->mesh, pbvh->verts, - vdata, - ldata, CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK), CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS), pbvh->face_sets_color_seed, @@ -1376,7 +1364,7 @@ static void pbvh_update_draw_buffer_cb(void *__restrict userdata, case PBVH_BMESH: GPU_pbvh_bmesh_buffers_update(pbvh->vbo_id, node->draw_buffers, - pbvh->bm, + pbvh->header.bm, node->bm_faces, node->bm_unique_verts, node->bm_other_verts, @@ -1405,15 +1393,15 @@ static void pbvh_check_draw_layout(PBVH *pbvh) pbvh->vbo_id = GPU_pbvh_make_format(); } - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_BMESH: - if (!pbvh->bm) { + if (!pbvh->header.bm) { /* BMesh hasn't been created yet */ return; } - vdata = &pbvh->bm->vdata; - ldata = &pbvh->bm->ldata; + vdata = &pbvh->header.bm->vdata; + ldata = &pbvh->header.bm->ldata; break; case PBVH_FACES: vdata = pbvh->vdata; @@ -1431,7 +1419,7 @@ static void pbvh_check_draw_layout(PBVH *pbvh) * (there's no guarantee there isn't another EEVEE viewport which would * free the draw buffers and corrupt the draw cache). */ - if (GPU_pbvh_attribute_names_update(pbvh->type, pbvh->vbo_id, vdata, ldata, false)) { + if (GPU_pbvh_attribute_names_update(pbvh->header.type, pbvh->vbo_id, vdata, ldata, false)) { /* attribute layout changed; force rebuild */ for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *node = pbvh->nodes + i; @@ -1451,14 +1439,14 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, pbvh->vbo_id = GPU_pbvh_make_format(); } - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_BMESH: - if (!pbvh->bm) { + if (!pbvh->header.bm) { /* BMesh hasn't been created yet */ return; } - vdata = &pbvh->bm->vdata; + vdata = &pbvh->header.bm->vdata; break; case PBVH_FACES: vdata = pbvh->vdata; @@ -1469,7 +1457,7 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, } UNUSED_VARS(vdata); - if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->type, PBVH_GRIDS, PBVH_BMESH)) { + if ((update_flag & PBVH_RebuildDrawBuffers) || ELEM(pbvh->header.type, PBVH_GRIDS, PBVH_BMESH)) { /* Free buffers uses OpenGL, so not in parallel. */ for (int n = 0; n < totnode; n++) { PBVHNode *node = nodes[n]; @@ -1477,11 +1465,11 @@ static void pbvh_update_draw_buffers(PBVH *pbvh, PBVHNode **nodes, int totnode, pbvh_free_draw_buffers(pbvh, node); } else if ((node->flag & PBVH_UpdateDrawBuffers) && node->draw_buffers) { - if (pbvh->type == PBVH_GRIDS) { + if (pbvh->header.type == PBVH_GRIDS) { GPU_pbvh_grid_buffers_update_free( node->draw_buffers, pbvh->grid_flag_mats, node->prim_indices); } - else if (pbvh->type == PBVH_BMESH) { + else if (pbvh->header.type == PBVH_BMESH) { GPU_pbvh_bmesh_buffers_update_free(node->draw_buffers); } } @@ -1602,9 +1590,12 @@ static void pbvh_faces_node_visibility_update(PBVH *pbvh, PBVHNode *node) BKE_pbvh_node_num_verts(pbvh, node, NULL, &totvert); BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); + if (pbvh->hide_vert == NULL) { + BKE_pbvh_node_fully_hidden_set(node, false); + return; + } for (i = 0; i < totvert; i++) { - MVert *v = &mvert[vert_indices[i]]; - if (!(v->flag & ME_HIDE)) { + if (!(pbvh->hide_vert[vert_indices[i]])) { BKE_pbvh_node_fully_hidden_set(node, false); return; } @@ -1795,15 +1786,10 @@ void BKE_pbvh_get_grid_updates(PBVH *pbvh, bool clear, void ***r_gridfaces, int /***************************** PBVH Access ***********************************/ -PBVHType BKE_pbvh_type(const PBVH *pbvh) -{ - return pbvh->type; -} - bool BKE_pbvh_has_faces(const PBVH *pbvh) { - if (pbvh->type == PBVH_BMESH) { - return (pbvh->bm->totface != 0); + if (pbvh->header.type == PBVH_BMESH) { + return (pbvh->header.bm->totface != 0); } return (pbvh->totprim != 0); @@ -1824,46 +1810,40 @@ void BKE_pbvh_bounding_box(const PBVH *pbvh, float min[3], float max[3]) BLI_bitmap **BKE_pbvh_grid_hidden(const PBVH *pbvh) { - BLI_assert(pbvh->type == PBVH_GRIDS); + BLI_assert(pbvh->header.type == PBVH_GRIDS); return pbvh->grid_hidden; } const CCGKey *BKE_pbvh_get_grid_key(const PBVH *pbvh) { - BLI_assert(pbvh->type == PBVH_GRIDS); + BLI_assert(pbvh->header.type == PBVH_GRIDS); return &pbvh->gridkey; } struct CCGElem **BKE_pbvh_get_grids(const PBVH *pbvh) { - BLI_assert(pbvh->type == PBVH_GRIDS); + BLI_assert(pbvh->header.type == PBVH_GRIDS); return pbvh->grids; } BLI_bitmap **BKE_pbvh_get_grid_visibility(const PBVH *pbvh) { - BLI_assert(pbvh->type == PBVH_GRIDS); + BLI_assert(pbvh->header.type == PBVH_GRIDS); return pbvh->grid_hidden; } int BKE_pbvh_get_grid_num_vertices(const PBVH *pbvh) { - BLI_assert(pbvh->type == PBVH_GRIDS); + BLI_assert(pbvh->header.type == PBVH_GRIDS); return pbvh->totgrid * pbvh->gridkey.grid_area; } int BKE_pbvh_get_grid_num_faces(const PBVH *pbvh) { - BLI_assert(pbvh->type == PBVH_GRIDS); + BLI_assert(pbvh->header.type == PBVH_GRIDS); return pbvh->totgrid * (pbvh->gridkey.grid_size - 1) * (pbvh->gridkey.grid_size - 1); } -BMesh *BKE_pbvh_get_bmesh(PBVH *pbvh) -{ - BLI_assert(pbvh->type == PBVH_BMESH); - return pbvh->bm; -} - /***************************** Node Access ***********************************/ void BKE_pbvh_node_mark_update(PBVHNode *node) @@ -1964,10 +1944,10 @@ bool BKE_pbvh_node_fully_unmasked_get(PBVHNode *node) return (node->flag & PBVH_Leaf) && (node->flag & PBVH_FullyUnmasked); } -void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, int index) +void BKE_pbvh_vert_tag_update_normal(PBVH *pbvh, PBVHVertRef vertex) { - BLI_assert(pbvh->type == PBVH_FACES); - pbvh->vert_bitmap[index] = true; + BLI_assert(pbvh->header.type == PBVH_FACES); + pbvh->vert_bitmap[vertex.i] = true; } void BKE_pbvh_node_get_loops(PBVH *pbvh, @@ -2004,7 +1984,7 @@ void BKE_pbvh_node_num_verts(PBVH *pbvh, PBVHNode *node, int *r_uniquevert, int { int tot; - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_GRIDS: tot = node->totprim * pbvh->gridkey.grid_area; if (r_totvert) { @@ -2042,7 +2022,7 @@ void BKE_pbvh_node_get_grids(PBVH *pbvh, int *r_gridsize, CCGElem ***r_griddata) { - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_GRIDS: if (r_grid_indices) { *r_grid_indices = node->prim_indices; @@ -2125,7 +2105,7 @@ void BKE_pbvh_node_get_bm_orco_data(PBVHNode *node, bool BKE_pbvh_node_has_vert_with_normal_update_tag(PBVH *pbvh, PBVHNode *node) { - BLI_assert(pbvh->type == PBVH_FACES); + BLI_assert(pbvh->header.type == PBVH_FACES); const int *verts = node->vert_indices; const int totvert = node->uniq_verts + node->face_verts; @@ -2299,7 +2279,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, float *depth, - int *r_active_vertex_index, + PBVHVertRef *r_active_vertex, int *r_active_face_index, float *r_face_normal) { @@ -2314,7 +2294,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, const MLoopTri *lt = &pbvh->looptri[faces[i]]; const int *face_verts = node->face_vert_indices[i]; - if (pbvh->respect_hide && paint_is_face_hidden(lt, vert, mloop)) { + if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) { continue; } @@ -2339,7 +2319,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, normal_tri_v3(r_face_normal, co[0], co[1], co[2]); } - if (r_active_vertex_index) { + if (r_active_vertex) { float location[3] = {0.0f}; madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); for (int j = 0; j < 3; j++) { @@ -2349,7 +2329,7 @@ static bool pbvh_faces_node_raycast(PBVH *pbvh, if (j == 0 || len_squared_v3v3(location, co[j]) < len_squared_v3v3(location, nearest_vertex_co)) { copy_v3_v3(nearest_vertex_co, co[j]); - *r_active_vertex_index = mloop[lt->tri[j]].v; + r_active_vertex->i = mloop[lt->tri[j]].v; *r_active_face_index = lt->poly; } } @@ -2367,7 +2347,7 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, float *depth, - int *r_active_vertex_index, + PBVHVertRef *r_active_vertex, int *r_active_grid_index, float *r_face_normal) { @@ -2419,7 +2399,7 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, normal_quad_v3(r_face_normal, co[0], co[1], co[2], co[3]); } - if (r_active_vertex_index) { + if (r_active_vertex) { float location[3] = {0.0}; madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); @@ -2434,8 +2414,8 @@ static bool pbvh_grids_node_raycast(PBVH *pbvh, len_squared_v3v3(location, nearest_vertex_co)) { copy_v3_v3(nearest_vertex_co, co[j]); - *r_active_vertex_index = gridkey->grid_area * grid_index + - (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]); + r_active_vertex->i = gridkey->grid_area * grid_index + + (y + y_it[j]) * gridkey->grid_size + (x + x_it[j]); } } } @@ -2462,7 +2442,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, const float ray_normal[3], struct IsectRayPrecalc *isect_precalc, float *depth, - int *active_vertex_index, + PBVHVertRef *active_vertex, int *active_face_grid_index, float *face_normal) { @@ -2472,7 +2452,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, return false; } - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_FACES: hit |= pbvh_faces_node_raycast(pbvh, node, @@ -2481,7 +2461,7 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, ray_normal, isect_precalc, depth, - active_vertex_index, + active_vertex, active_face_grid_index, face_normal); break; @@ -2493,19 +2473,19 @@ bool BKE_pbvh_node_raycast(PBVH *pbvh, ray_normal, isect_precalc, depth, - active_vertex_index, + active_vertex, active_face_grid_index, face_normal); break; case PBVH_BMESH: - BM_mesh_elem_index_ensure(pbvh->bm, BM_VERT); + BM_mesh_elem_index_ensure(pbvh->header.bm, BM_VERT); hit = pbvh_bmesh_node_raycast(node, ray_start, ray_normal, isect_precalc, depth, use_origco, - active_vertex_index, + active_vertex, face_normal); break; } @@ -2623,7 +2603,7 @@ static bool pbvh_faces_node_nearest_to_ray(PBVH *pbvh, const MLoopTri *lt = &pbvh->looptri[faces[i]]; const int *face_verts = node->face_vert_indices[i]; - if (pbvh->respect_hide && paint_is_face_hidden(lt, vert, mloop)) { + if (pbvh->respect_hide && paint_is_face_hidden(lt, pbvh->hide_vert, mloop)) { continue; } @@ -2729,7 +2709,7 @@ bool BKE_pbvh_node_find_nearest_to_ray(PBVH *pbvh, return false; } - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_FACES: hit |= pbvh_faces_node_nearest_to_ray( pbvh, node, origco, ray_start, ray_normal, depth, dist_sq); @@ -2820,13 +2800,13 @@ void BKE_pbvh_update_normals(PBVH *pbvh, struct SubdivCCG *subdiv_ccg) pbvh, update_search_cb, POINTER_FROM_INT(PBVH_UpdateNormals), &nodes, &totnode); if (totnode > 0) { - if (pbvh->type == PBVH_BMESH) { + if (pbvh->header.type == PBVH_BMESH) { pbvh_bmesh_normals_update(nodes, totnode); } - else if (pbvh->type == PBVH_FACES) { + else if (pbvh->header.type == PBVH_FACES) { pbvh_faces_update_normals(pbvh, nodes, totnode); } - else if (pbvh->type == PBVH_GRIDS) { + else if (pbvh->header.type == PBVH_GRIDS) { struct CCGFace **faces; int num_faces; BKE_pbvh_get_grid_updates(pbvh, true, (void ***)&faces, &num_faces); @@ -2991,7 +2971,7 @@ void BKE_pbvh_vert_coords_apply(PBVH *pbvh, const float (*vertCos)[3], const int /* no need for float comparison here (memory is exactly equal or not) */ if (memcmp(mvert->co, vertCos[a], sizeof(float[3])) != 0) { copy_v3_v3(mvert->co, vertCos[a]); - BKE_pbvh_vert_tag_update_normal(pbvh, a); + BKE_pbvh_vert_tag_update_normal(pbvh, BKE_pbvh_make_vref(a)); } } @@ -3108,6 +3088,7 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->no = NULL; vi->fno = NULL; vi->mvert = NULL; + vi->vertex.i = 0LL; vi->respect_hide = pbvh->respect_hide; if (pbvh->respect_hide == false) { @@ -3134,10 +3115,10 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m vi->vert_indices = vert_indices; vi->mverts = verts; - if (pbvh->type == PBVH_BMESH) { + if (pbvh->header.type == PBVH_BMESH) { BLI_gsetIterator_init(&vi->bm_unique_verts, node->bm_unique_verts); BLI_gsetIterator_init(&vi->bm_other_verts, node->bm_other_verts); - vi->bm_vdata = &pbvh->bm->vdata; + vi->bm_vdata = &pbvh->header.bm->vdata; vi->cd_vert_mask_offset = CustomData_get_offset(vi->bm_vdata, CD_PAINT_MASK); } @@ -3147,8 +3128,9 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m } vi->mask = NULL; - if (pbvh->type == PBVH_FACES) { + if (pbvh->header.type == PBVH_FACES) { vi->vert_normals = pbvh->vert_normals; + vi->hide_vert = pbvh->hide_vert; vi->vmask = CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK); } @@ -3156,13 +3138,14 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m bool pbvh_has_mask(const PBVH *pbvh) { - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_GRIDS: return (pbvh->gridkey.has_mask != 0); case PBVH_FACES: return (pbvh->vdata && CustomData_get_layer(pbvh->vdata, CD_PAINT_MASK)); case PBVH_BMESH: - return (pbvh->bm && (CustomData_get_offset(&pbvh->bm->vdata, CD_PAINT_MASK) != -1)); + return (pbvh->header.bm && + (CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK) != -1)); } return false; @@ -3170,7 +3153,7 @@ bool pbvh_has_mask(const PBVH *pbvh) bool pbvh_has_face_sets(PBVH *pbvh) { - switch (pbvh->type) { + switch (pbvh->header.type) { case PBVH_GRIDS: return (pbvh->pdata && CustomData_get_layer(pbvh->pdata, CD_SCULPT_FACE_SETS)); case PBVH_FACES: @@ -3218,16 +3201,37 @@ void BKE_pbvh_parallel_range_settings(TaskParallelSettings *settings, MVert *BKE_pbvh_get_verts(const PBVH *pbvh) { - BLI_assert(pbvh->type == PBVH_FACES); + BLI_assert(pbvh->header.type == PBVH_FACES); return pbvh->verts; } const float (*BKE_pbvh_get_vert_normals(const PBVH *pbvh))[3] { - BLI_assert(pbvh->type == PBVH_FACES); + BLI_assert(pbvh->header.type == PBVH_FACES); return pbvh->vert_normals; } +const bool *BKE_pbvh_get_vert_hide(const PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + return pbvh->hide_vert; +} + +bool *BKE_pbvh_get_vert_hide_for_write(PBVH *pbvh) +{ + BLI_assert(pbvh->header.type == PBVH_FACES); + if (pbvh->hide_vert) { + return pbvh->hide_vert; + } + pbvh->hide_vert = CustomData_get_layer_named(&pbvh->mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + if (pbvh->hide_vert) { + return pbvh->hide_vert; + } + pbvh->hide_vert = (bool *)CustomData_add_layer_named( + &pbvh->mesh->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, pbvh->mesh->totvert, ".hide_vert"); + return pbvh->hide_vert; +} + void BKE_pbvh_subdiv_cgg_set(PBVH *pbvh, SubdivCCG *subdiv_ccg) { pbvh->subdiv_ccg = subdiv_ccg; @@ -3242,6 +3246,7 @@ void BKE_pbvh_respect_hide_set(PBVH *pbvh, bool respect_hide) { pbvh->respect_hide = respect_hide; } + bool BKE_pbvh_is_drawing(const PBVH *pbvh) { return pbvh->is_drawing; diff --git a/source/blender/blenkernel/intern/pbvh.cc b/source/blender/blenkernel/intern/pbvh.cc index dec93826b9b..70aeb10f087 100644 --- a/source/blender/blenkernel/intern/pbvh.cc +++ b/source/blender/blenkernel/intern/pbvh.cc @@ -86,10 +86,12 @@ template<> void from_float(const float src[4], MPropCol &dst) } template<typename T> -static void pbvh_vertex_color_get(const PBVH &pbvh, int vertex, float r_color[4]) +static void pbvh_vertex_color_get(const PBVH &pbvh, PBVHVertRef vertex, float r_color[4]) { + int index = vertex.i; + if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { - const MeshElemMap &melem = pbvh.pmap[vertex]; + const MeshElemMap &melem = pbvh.pmap[index]; int count = 0; zero_v4(r_color); @@ -99,7 +101,7 @@ static void pbvh_vertex_color_get(const PBVH &pbvh, int vertex, float r_color[4] Span<MLoop> loops{pbvh.mloop + mp.loopstart, mp.totloop}; for (const int i_loop : IndexRange(mp.totloop)) { - if (loops[i_loop].v == vertex) { + if (loops[i_loop].v == index) { float temp[4]; to_float(colors[i_loop], temp); @@ -114,15 +116,17 @@ static void pbvh_vertex_color_get(const PBVH &pbvh, int vertex, float r_color[4] } } else { - to_float(static_cast<T *>(pbvh.color_layer->data)[vertex], r_color); + to_float(static_cast<T *>(pbvh.color_layer->data)[index], r_color); } } template<typename T> -static void pbvh_vertex_color_set(PBVH &pbvh, int vertex, const float color[4]) +static void pbvh_vertex_color_set(PBVH &pbvh, PBVHVertRef vertex, const float color[4]) { + int index = vertex.i; + if (pbvh.color_domain == ATTR_DOMAIN_CORNER) { - const MeshElemMap &melem = pbvh.pmap[vertex]; + const MeshElemMap &melem = pbvh.pmap[index]; for (const int i_poly : Span(melem.indices, melem.count)) { const MPoly &mp = pbvh.mpoly[i_poly]; @@ -130,21 +134,21 @@ static void pbvh_vertex_color_set(PBVH &pbvh, int vertex, const float color[4]) Span<MLoop> loops{pbvh.mloop + mp.loopstart, mp.totloop}; for (const int i_loop : IndexRange(mp.totloop)) { - if (loops[i_loop].v == vertex) { + if (loops[i_loop].v == index) { from_float(color, colors[i_loop]); } } } } else { - from_float(color, static_cast<T *>(pbvh.color_layer->data)[vertex]); + from_float(color, static_cast<T *>(pbvh.color_layer->data)[index]); } } } // namespace blender::bke extern "C" { -void BKE_pbvh_vertex_color_get(const PBVH *pbvh, int vertex, float r_color[4]) +void BKE_pbvh_vertex_color_get(const PBVH *pbvh, PBVHVertRef vertex, float r_color[4]) { blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) { using T = decltype(dummy); @@ -152,7 +156,7 @@ void BKE_pbvh_vertex_color_get(const PBVH *pbvh, int vertex, float r_color[4]) }); } -void BKE_pbvh_vertex_color_set(PBVH *pbvh, int vertex, const float color[4]) +void BKE_pbvh_vertex_color_set(PBVH *pbvh, PBVHVertRef vertex, const float color[4]) { blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) { using T = decltype(dummy); @@ -202,7 +206,7 @@ void BKE_pbvh_store_colors_vertex(PBVH *pbvh, blender::bke::to_static_color_type(eCustomDataType(pbvh->color_layer->type), [&](auto dummy) { using T = decltype(dummy); for (const int i : IndexRange(indices_num)) { - blender::bke::pbvh_vertex_color_get<T>(*pbvh, indices[i], r_colors[i]); + blender::bke::pbvh_vertex_color_get<T>(*pbvh, BKE_pbvh_make_vref(indices[i]), r_colors[i]); } }); } diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index 112fd01c699..c4993684100 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -400,7 +400,7 @@ static bool pbvh_bmesh_node_limit_ensure(PBVH *pbvh, int node_index) BM_elem_index_set(f, i); /* set_dirty! */ } /* Likely this is already dirty. */ - pbvh->bm->elem_index_dirty |= BM_FACE; + pbvh->header.bm->elem_index_dirty |= BM_FACE; pbvh_bmesh_node_split(pbvh, bbc_array, node_index); @@ -484,8 +484,8 @@ static BMVert *pbvh_bmesh_vert_create(PBVH *pbvh, BLI_assert((pbvh->totnode == 1 || node_index) && node_index <= pbvh->totnode); /* avoid initializing customdata because its quite involved */ - BMVert *v = BM_vert_create(pbvh->bm, co, NULL, BM_CREATE_SKIP_CD); - CustomData_bmesh_set_default(&pbvh->bm->vdata, &v->head.data); + BMVert *v = BM_vert_create(pbvh->header.bm, co, NULL, BM_CREATE_SKIP_CD); + CustomData_bmesh_set_default(&pbvh->header.bm->vdata, &v->head.data); /* This value is logged below */ copy_v3_v3(v->no, no); @@ -512,7 +512,7 @@ static BMFace *pbvh_bmesh_face_create( /* ensure we never add existing face */ BLI_assert(!BM_face_exists(v_tri, 3)); - BMFace *f = BM_face_create(pbvh->bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP); + BMFace *f = BM_face_create(pbvh->header.bm, v_tri, e_tri, 3, f_example, BM_CREATE_NOP); f->head.hflag = f_example->head.hflag; BLI_gset_insert(node->bm_faces, f); @@ -1185,22 +1185,22 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, v_tri[0] = v1; v_tri[1] = v_new; v_tri[2] = v_opp; - bm_edges_from_tri(pbvh->bm, v_tri, e_tri); + bm_edges_from_tri(pbvh->header.bm, v_tri, e_tri); f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj); long_edge_queue_face_add(eq_ctx, f_new); v_tri[0] = v_new; v_tri[1] = v2; /* v_tri[2] = v_opp; */ /* unchanged */ - e_tri[0] = BM_edge_create(pbvh->bm, v_tri[0], v_tri[1], NULL, BM_CREATE_NO_DOUBLE); + e_tri[0] = BM_edge_create(pbvh->header.bm, v_tri[0], v_tri[1], NULL, BM_CREATE_NO_DOUBLE); e_tri[2] = e_tri[1]; /* switched */ - e_tri[1] = BM_edge_create(pbvh->bm, v_tri[1], v_tri[2], NULL, BM_CREATE_NO_DOUBLE); + e_tri[1] = BM_edge_create(pbvh->header.bm, v_tri[1], v_tri[2], NULL, BM_CREATE_NO_DOUBLE); f_new = pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f_adj); long_edge_queue_face_add(eq_ctx, f_new); /* Delete original */ pbvh_bmesh_face_remove(pbvh, f_adj); - BM_face_kill(pbvh->bm, f_adj); + BM_face_kill(pbvh->header.bm, f_adj); /* Ensure new vertex is in the node */ if (!BLI_gset_haskey(pbvh->nodes[ni].bm_unique_verts, v_new)) { @@ -1217,7 +1217,7 @@ static void pbvh_bmesh_split_edge(EdgeQueueContext *eq_ctx, } } - BM_edge_kill(pbvh->bm, e); + BM_edge_kill(pbvh->header.bm, e); } static bool pbvh_bmesh_subdivide_long_edges(EdgeQueueContext *eq_ctx, @@ -1303,12 +1303,12 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, BMFace *f_adj = l_adj->f; pbvh_bmesh_face_remove(pbvh, f_adj); - BM_face_kill(pbvh->bm, f_adj); + BM_face_kill(pbvh->header.bm, f_adj); } /* Kill the edge */ BLI_assert(BM_edge_is_wire(e)); - BM_edge_kill(pbvh->bm, e); + BM_edge_kill(pbvh->header.bm, e); /* For all remaining faces of v_del, create a new face that is the * same except it uses v_conn instead of v_del */ @@ -1355,7 +1355,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, BMEdge *e_tri[3]; PBVHNode *n = pbvh_bmesh_node_from_face(pbvh, f); int ni = n - pbvh->nodes; - bm_edges_from_tri(pbvh->bm, v_tri, e_tri); + bm_edges_from_tri(pbvh->header.bm, v_tri, e_tri); pbvh_bmesh_face_create(pbvh, ni, v_tri, e_tri, f); /* Ensure that v_conn is in the new face's node */ @@ -1388,13 +1388,13 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, /* Remove the face */ pbvh_bmesh_face_remove(pbvh, f_del); - BM_face_kill(pbvh->bm, f_del); + BM_face_kill(pbvh->header.bm, f_del); /* Check if any of the face's edges are now unused by any * face, if so delete them */ for (int j = 0; j < 3; j++) { if (BM_edge_is_wire(e_tri[j])) { - BM_edge_kill(pbvh->bm, e_tri[j]); + BM_edge_kill(pbvh->header.bm, e_tri[j]); } } @@ -1410,7 +1410,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, v_conn = NULL; } BLI_ghash_insert(deleted_verts, v_tri[j], NULL); - BM_vert_kill(pbvh->bm, v_tri[j]); + BM_vert_kill(pbvh->header.bm, v_tri[j]); } } } @@ -1437,7 +1437,7 @@ static void pbvh_bmesh_collapse_edge(PBVH *pbvh, BM_log_vert_removed(pbvh->bm_log, v_del, eq_ctx->cd_vert_mask_offset); /* v_conn == NULL is OK */ BLI_ghash_insert(deleted_verts, v_del, v_conn); - BM_vert_kill(pbvh->bm, v_del); + BM_vert_kill(pbvh->header.bm, v_del); } static bool pbvh_bmesh_collapse_short_edges(EdgeQueueContext *eq_ctx, @@ -1501,7 +1501,7 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, struct IsectRayPrecalc *isect_precalc, float *depth, bool use_original, - int *r_active_vertex_index, + PBVHVertRef *r_active_vertex, float *r_face_normal) { bool hit = false; @@ -1538,14 +1538,14 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, normal_tri_v3(r_face_normal, v_tri[0]->co, v_tri[1]->co, v_tri[2]->co); } - if (r_active_vertex_index) { + if (r_active_vertex) { float location[3] = {0.0f}; madd_v3_v3v3fl(location, ray_start, ray_normal, *depth); for (int j = 0; j < 3; j++) { if (len_squared_v3v3(location, v_tri[j]->co) < len_squared_v3v3(location, nearest_vertex_co)) { copy_v3_v3(nearest_vertex_co, v_tri[j]->co); - *r_active_vertex_index = BM_elem_index_get(v_tri[j]); + r_active_vertex->i = (intptr_t)v_tri[j]; } } } @@ -1872,11 +1872,11 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, { pbvh->cd_vert_node_offset = cd_vert_node_offset; pbvh->cd_face_node_offset = cd_face_node_offset; - pbvh->bm = bm; + pbvh->header.bm = bm; BKE_pbvh_bmesh_detail_size_set(pbvh, 0.75); - pbvh->type = PBVH_BMESH; + pbvh->header.type = PBVH_BMESH; pbvh->bm_log = log; /* TODO: choose leaf limit better */ @@ -1951,7 +1951,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, /* 2 is enough for edge faces - manifold edge */ BLI_buffer_declare_static(BMLoop *, edge_loops, BLI_BUFFER_NOP, 2); BLI_buffer_declare_static(BMFace *, deleted_faces, BLI_BUFFER_NOP, 32); - const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->bm->vdata, CD_PAINT_MASK); + const int cd_vert_mask_offset = CustomData_get_offset(&pbvh->header.bm->vdata, CD_PAINT_MASK); const int cd_vert_node_offset = pbvh->cd_vert_node_offset; const int cd_face_node_offset = pbvh->cd_face_node_offset; @@ -1967,7 +1967,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, EdgeQueueContext eq_ctx = { &q, queue_pool, - pbvh->bm, + pbvh->header.bm, cd_vert_mask_offset, cd_vert_node_offset, cd_face_node_offset, @@ -1986,7 +1986,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, EdgeQueueContext eq_ctx = { &q, queue_pool, - pbvh->bm, + pbvh->header.bm, cd_vert_mask_offset, cd_vert_node_offset, cd_face_node_offset, @@ -2126,13 +2126,13 @@ static void pbvh_bmesh_print(PBVH *pbvh) BMIter iter; BMFace *f; - BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { fprintf(stderr, " %d -> %d\n", BM_elem_index_get(f), pbvh_bmesh_node_index_from_face(pbvh, f)); } fprintf(stderr, "bm_vert_to_node:\n"); BMVert *v; - BM_ITER_MESH (v, &iter, pbvh->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { fprintf(stderr, " %d -> %d\n", BM_elem_index_get(v), pbvh_bmesh_node_index_from_vert(pbvh, v)); } @@ -2171,21 +2171,21 @@ static void print_flag_factors(int flag) static void pbvh_bmesh_verify(PBVH *pbvh) { /* build list of faces & verts to lookup */ - GSet *faces_all = BLI_gset_ptr_new_ex(__func__, pbvh->bm->totface); + GSet *faces_all = BLI_gset_ptr_new_ex(__func__, pbvh->header.bm->totface); BMIter iter; { BMFace *f; - BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { BLI_assert(BM_ELEM_CD_GET_INT(f, pbvh->cd_face_node_offset) != DYNTOPO_NODE_NONE); BLI_gset_insert(faces_all, f); } } - GSet *verts_all = BLI_gset_ptr_new_ex(__func__, pbvh->bm->totvert); + GSet *verts_all = BLI_gset_ptr_new_ex(__func__, pbvh->header.bm->totvert); { BMVert *v; - BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) { + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) != DYNTOPO_NODE_NONE) { BLI_gset_insert(verts_all, v); } @@ -2207,7 +2207,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh) { BMFace *f; - BM_ITER_MESH (f, &iter, pbvh->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH (f, &iter, pbvh->header.bm, BM_FACES_OF_MESH) { BMIter bm_iter; BMVert *v; PBVHNode *n = pbvh_bmesh_node_lookup(pbvh, f); @@ -2240,7 +2240,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh) /* Check verts */ { BMVert *v; - BM_ITER_MESH (v, &iter, pbvh->bm, BM_VERTS_OF_MESH) { + BM_ITER_MESH (v, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { /* vertex isn't tracked */ if (BM_ELEM_CD_GET_INT(v, pbvh->cd_vert_node_offset) == DYNTOPO_NODE_NONE) { continue; @@ -2286,7 +2286,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh) # if 0 /* check that every vert belongs somewhere */ /* Slow */ - BM_ITER_MESH (vi, &iter, pbvh->bm, BM_VERTS_OF_MESH) { + BM_ITER_MESH (vi, &iter, pbvh->header.bm, BM_VERTS_OF_MESH) { bool has_unique = false; for (int i = 0; i < pbvh->totnode; i++) { PBVHNode *n = &pbvh->nodes[i]; @@ -2299,7 +2299,7 @@ static void pbvh_bmesh_verify(PBVH *pbvh) } /* If totvert differs from number of verts inside the hash. hash-totvert is checked above. */ - BLI_assert(vert_count == pbvh->bm->totvert); + BLI_assert(vert_count == pbvh->header.bm->totvert); # endif /* Check that node elements are recorded in the top level */ diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index a4ac2744a73..5babfd3acbe 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -130,7 +130,7 @@ typedef enum { PBVH_DYNTOPO_SMOOTH_SHADING = 1 } PBVHFlags; typedef struct PBVHBMeshLog PBVHBMeshLog; struct PBVH { - PBVHType type; + struct PBVHPublic header; PBVHFlags flags; PBVHNode *nodes; @@ -144,10 +144,11 @@ struct PBVH { int leaf_limit; /* Mesh data */ - const struct Mesh *mesh; + struct Mesh *mesh; /* NOTE: Normals are not `const` because they can be updated for drawing by sculpt code. */ float (*vert_normals)[3]; + bool *hide_vert; struct MVert *verts; const struct MPoly *mpoly; const struct MLoop *mloop; @@ -183,7 +184,6 @@ struct PBVH { bool respect_hide; /* Dynamic topology */ - BMesh *bm; float bm_max_edge_len; float bm_min_edge_len; int cd_vert_node_offset; @@ -265,7 +265,7 @@ bool pbvh_bmesh_node_raycast(PBVHNode *node, struct IsectRayPrecalc *isect_precalc, float *dist, bool use_original, - int *r_active_vertex_index, + PBVHVertRef *r_active_vertex, float *r_face_normal); bool pbvh_bmesh_node_nearest_to_ray(PBVHNode *node, const float ray_start[3], diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index aaa6baac1ff..4d41471e1fb 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -2501,7 +2501,7 @@ static bool check_rendered_viewport_visible(Main *bmain) return false; } -/* TODO(campbell): shouldn't we be able to use 'DEG_get_view_layer' here? +/* TODO(@campbellbarton): shouldn't we be able to use 'DEG_get_view_layer' here? * Currently this is nullptr on load, so don't. */ static void prepare_mesh_for_viewport_render(Main *bmain, const ViewLayer *view_layer) { diff --git a/source/blender/blenkernel/intern/shader_fx.c b/source/blender/blenkernel/intern/shader_fx.c index 51ebd232978..745bd2a97e6 100644 --- a/source/blender/blenkernel/intern/shader_fx.c +++ b/source/blender/blenkernel/intern/shader_fx.c @@ -70,7 +70,8 @@ ShaderFxData *BKE_shaderfx_new(int type) fx->type = type; fx->mode = eShaderFxMode_Realtime | eShaderFxMode_Render; fx->flag = eShaderFxFlag_OverrideLibrary_Local; - fx->ui_expand_flag = 1; /* Expand only the parent panel by default. */ + /* Expand only the parent panel by default. */ + fx->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; if (fxi->flags & eShaderFxTypeFlag_EnableInEditmode) { fx->mode |= eShaderFxMode_Editmode; diff --git a/source/blender/blenkernel/intern/subdiv_converter_mesh.c b/source/blender/blenkernel/intern/subdiv_converter_mesh.c index 12a5f00a68b..9c6d507d42c 100644 --- a/source/blender/blenkernel/intern/subdiv_converter_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_converter_mesh.c @@ -205,7 +205,15 @@ static void precalc_uv_layer(const OpenSubdiv_Converter *converter, const int la mesh->totloop, sizeof(int), "loop uv vertex index"); } UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create( - mpoly, mloop, mloopuv, num_poly, num_vert, limit, false, true); + mpoly, + (const bool *)CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"), + mloop, + mloopuv, + num_poly, + num_vert, + limit, + false, + true); /* NOTE: First UV vertex is supposed to be always marked as separate. */ storage->num_uv_coordinates = -1; for (int vertex_index = 0; vertex_index < num_vert; vertex_index++) { diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index efabb4f039a..f19aac68d35 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -284,7 +284,8 @@ static int ss_sync_from_uv(CCGSubSurf *ss, * UV map in really simple cases with mirror + subsurf, see second part of T44530. * Also, initially intention is to treat merged vertices from mirror modifier as seams. * This fixes a very old regression (2.49 was correct here) */ - vmap = BKE_mesh_uv_vert_map_create(mpoly, mloop, mloopuv, totface, totvert, limit, false, true); + vmap = BKE_mesh_uv_vert_map_create( + mpoly, NULL, mloop, mloopuv, totface, totvert, limit, false, true); if (!vmap) { return 0; } diff --git a/source/blender/blenkernel/intern/type_conversions.cc b/source/blender/blenkernel/intern/type_conversions.cc index 0b5d6ad7b10..a01f5d19088 100644 --- a/source/blender/blenkernel/intern/type_conversions.cc +++ b/source/blender/blenkernel/intern/type_conversions.cc @@ -367,20 +367,38 @@ void DataTypeConversions::convert_to_uninitialized(const CPPType &from_type, functions->convert_single_to_uninitialized(from_value, to_value); } +static void call_convert_to_uninitialized_fn(const GVArray &from, + const fn::MultiFunction &fn, + const IndexMask mask, + GMutableSpan to) +{ + fn::MFParamsBuilder params{fn, from.size()}; + params.add_readonly_single_input(from); + params.add_uninitialized_single_output(to); + fn::MFContextBuilder context; + fn.call_auto(mask, params, context); +} + +static void call_convert_to_uninitialized_fn(const GVArray &from, + const fn::MultiFunction &fn, + GMutableSpan to) +{ + call_convert_to_uninitialized_fn(from, fn, IndexMask(from.size()), to); +} + void DataTypeConversions::convert_to_initialized_n(GSpan from_span, GMutableSpan to_span) const { const CPPType &from_type = from_span.type(); const CPPType &to_type = to_span.type(); + BLI_assert(from_span.size() == to_span.size()); BLI_assert(this->is_convertible(from_type, to_type)); + const fn::MultiFunction *fn = this->get_conversion_multi_function( MFDataType::ForSingle(from_type), MFDataType::ForSingle(to_type)); - fn::MFParamsBuilder params{*fn, from_span.size()}; - params.add_readonly_single_input(from_span); + to_type.destruct_n(to_span.data(), to_span.size()); - params.add_uninitialized_single_output(to_span); - fn::MFContextBuilder context; - fn->call_auto(IndexRange(from_span.size()), params, context); + call_convert_to_uninitialized_fn(GVArray::ForSpan(from_span), *fn, to_span); } class GVArray_For_ConvertedGVArray : public GVArrayImpl { @@ -414,6 +432,20 @@ class GVArray_For_ConvertedGVArray : public GVArrayImpl { old_to_new_conversions_.convert_single_to_uninitialized(buffer, r_value); from_type_.destruct(buffer); } + + void materialize(const IndexMask mask, void *dst) const override + { + type_->destruct_n(dst, mask.min_array_size()); + this->materialize_to_uninitialized(mask, dst); + } + + void materialize_to_uninitialized(const IndexMask mask, void *dst) const override + { + call_convert_to_uninitialized_fn(varray_, + *old_to_new_conversions_.multi_function, + mask, + {this->type(), dst, mask.min_array_size()}); + } }; class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArrayImpl { @@ -458,6 +490,20 @@ class GVMutableArray_For_ConvertedGVMutableArray : public GVMutableArrayImpl { new_to_old_conversions_.convert_single_to_uninitialized(value, buffer); varray_.set_by_relocate(index, buffer); } + + void materialize(const IndexMask mask, void *dst) const override + { + type_->destruct_n(dst, mask.min_array_size()); + this->materialize_to_uninitialized(mask, dst); + } + + void materialize_to_uninitialized(const IndexMask mask, void *dst) const override + { + call_convert_to_uninitialized_fn(varray_, + *old_to_new_conversions_.multi_function, + mask, + {this->type(), dst, mask.min_array_size()}); + } }; GVArray DataTypeConversions::try_convert(GVArray varray, const CPPType &to_type) const @@ -495,9 +541,8 @@ fn::GField DataTypeConversions::try_convert(fn::GField field, const CPPType &to_ if (!this->is_convertible(from_type, to_type)) { return {}; } - const fn::MultiFunction &fn = - *bke::get_implicit_type_conversions().get_conversion_multi_function( - fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)); + const fn::MultiFunction &fn = *this->get_conversion_multi_function( + fn::MFDataType::ForSingle(from_type), fn::MFDataType::ForSingle(to_type)); return {std::make_shared<fn::FieldOperation>(fn, Vector<fn::GField>{std::move(field)})}; } diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 0265dd037b1..88e7db1fe6c 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -456,12 +456,12 @@ WorkSpaceLayout *BKE_workspace_layout_iter_circular(const WorkSpace *workspace, WorkSpaceLayout *iter_layout; if (iter_backward) { - LISTBASE_CIRCULAR_BACKWARD_BEGIN (&workspace->layouts, iter_layout, start) { + LISTBASE_CIRCULAR_BACKWARD_BEGIN (WorkSpaceLayout *, &workspace->layouts, iter_layout, start) { if (!callback(iter_layout, arg)) { return iter_layout; } } - LISTBASE_CIRCULAR_BACKWARD_END(&workspace->layouts, iter_layout, start); + LISTBASE_CIRCULAR_BACKWARD_END(WorkSpaceLayout *, &workspace->layouts, iter_layout, start); } else { LISTBASE_CIRCULAR_FORWARD_BEGIN (&workspace->layouts, iter_layout, start) { diff --git a/source/blender/blenkernel/nla_private.h b/source/blender/blenkernel/nla_private.h index 41d1eef733c..c6fbdcc542c 100644 --- a/source/blender/blenkernel/nla_private.h +++ b/source/blender/blenkernel/nla_private.h @@ -128,7 +128,7 @@ typedef struct NlaEvalData { int num_channels; NlaEvalSnapshot base_snapshot; - /* Evaluation result shapshot. */ + /* Evaluation result snapshot. */ NlaEvalSnapshot eval_snapshot; } NlaEvalData; diff --git a/source/blender/blenlib/BLI_cpp_type.hh b/source/blender/blenlib/BLI_cpp_type.hh index cc48b456da7..8cf5ead1c7b 100644 --- a/source/blender/blenlib/BLI_cpp_type.hh +++ b/source/blender/blenlib/BLI_cpp_type.hh @@ -20,7 +20,7 @@ * cost of longer compile time, a larger binary and the complexity that comes from using * templates). * - If the code is not performance sensitive, it usually makes sense to use #CPPType instead. - * - Sometimes a combination can make sense. Optimized code can be be generated at compile-time for + * - Sometimes a combination can make sense. Optimized code can be generated at compile-time for * some types, while there is a fallback code path using #CPPType for all other types. * #CPPType::to_static_type allows dispatching between both versions based on the type. * diff --git a/source/blender/blenlib/BLI_index_range.hh b/source/blender/blenlib/BLI_index_range.hh index 6fcc560d856..2b290e1ba7d 100644 --- a/source/blender/blenlib/BLI_index_range.hh +++ b/source/blender/blenlib/BLI_index_range.hh @@ -90,10 +90,10 @@ class IndexRange { return *this; } - constexpr Iterator operator++(int) const + constexpr Iterator operator++(int) { Iterator copied_iterator = *this; - ++copied_iterator; + ++(*this); return copied_iterator; } diff --git a/source/blender/blenlib/BLI_listbase.h b/source/blender/blenlib/BLI_listbase.h index 237fcdae8b9..9322fa4c85b 100644 --- a/source/blender/blenlib/BLI_listbase.h +++ b/source/blender/blenlib/BLI_listbase.h @@ -320,13 +320,13 @@ struct LinkData *BLI_genericNodeN(void *data); } \ ((void)0) -#define LISTBASE_CIRCULAR_BACKWARD_BEGIN(lb, lb_iter, lb_init) \ - if ((lb)->last && (lb_init || (lb_init = (lb)->last))) { \ +#define LISTBASE_CIRCULAR_BACKWARD_BEGIN(type, lb, lb_iter, lb_init) \ + if ((lb)->last && (lb_init || (lb_init = (type)(lb)->last))) { \ lb_iter = lb_init; \ do { -#define LISTBASE_CIRCULAR_BACKWARD_END(lb, lb_iter, lb_init) \ +#define LISTBASE_CIRCULAR_BACKWARD_END(type, lb, lb_iter, lb_init) \ } \ - while ((lb_iter = (lb_iter)->prev ? (lb_iter)->prev : (lb)->last), (lb_iter != lb_init)) \ + while ((lb_iter = (lb_iter)->prev ? (lb_iter)->prev : (type)(lb)->last), (lb_iter != lb_init)) \ ; \ } \ ((void)0) diff --git a/source/blender/blenlib/BLI_listbase_wrapper.hh b/source/blender/blenlib/BLI_listbase_wrapper.hh index 25e029a5616..2d631cb2441 100644 --- a/source/blender/blenlib/BLI_listbase_wrapper.hh +++ b/source/blender/blenlib/BLI_listbase_wrapper.hh @@ -50,7 +50,7 @@ template<typename T> class ListBaseWrapper { Iterator operator++(int) { Iterator iterator = *this; - ++*this; + ++(*this); return iterator; } diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 55233676ed8..95d1e344894 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -669,10 +669,10 @@ class Map { return *this; } - BaseIterator operator++(int) const + BaseIterator operator++(int) { BaseIterator copied_iterator = *this; - ++copied_iterator; + ++(*this); return copied_iterator; } diff --git a/source/blender/blenlib/BLI_math_rotation.h b/source/blender/blenlib/BLI_math_rotation.h index 3987c9daf0a..b8ab74d95ff 100644 --- a/source/blender/blenlib/BLI_math_rotation.h +++ b/source/blender/blenlib/BLI_math_rotation.h @@ -189,7 +189,7 @@ void mat3_to_quat_is_ok(float q[4], const float mat[3][3]); * \endcode * * \param numerator: An integer factor in [0..denominator] (inclusive). - * \param denominator: The faction denominator (typically the number of segments of the circle). + * \param denominator: The fraction denominator (typically the number of segments of the circle). * \param r_sin: The resulting sine. * \param r_cos: The resulting cosine. */ diff --git a/source/blender/blenlib/BLI_scanfill.h b/source/blender/blenlib/BLI_scanfill.h index 04ac7cb05e4..b5b100ac27d 100644 --- a/source/blender/blenlib/BLI_scanfill.h +++ b/source/blender/blenlib/BLI_scanfill.h @@ -82,7 +82,7 @@ struct ScanFillEdge *BLI_scanfill_edge_add(ScanFillContext *sf_ctx, struct ScanFillVert *v2); enum { - /* NOTE(campbell): using BLI_SCANFILL_CALC_REMOVE_DOUBLES + /* NOTE(@campbellbarton): using #BLI_SCANFILL_CALC_REMOVE_DOUBLES * Assumes ordered edges, otherwise we risk an eternal loop * removing double verts. */ BLI_SCANFILL_CALC_REMOVE_DOUBLES = (1 << 1), diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index 62de4b79e41..a1b6ad9754e 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -427,10 +427,10 @@ class Set { return *this; } - Iterator operator++(int) const + Iterator operator++(int) { Iterator copied_iterator = *this; - ++copied_iterator; + ++(*this); return copied_iterator; } diff --git a/source/blender/blenlib/BLI_virtual_array.hh b/source/blender/blenlib/BLI_virtual_array.hh index 438fcc4b8f7..7eab960b302 100644 --- a/source/blender/blenlib/BLI_virtual_array.hh +++ b/source/blender/blenlib/BLI_virtual_array.hh @@ -315,6 +315,12 @@ template<typename T> class VArrayImpl_For_Span_final final : public VArrayImpl_F public: using VArrayImpl_For_Span<T>::VArrayImpl_For_Span; + VArrayImpl_For_Span_final(const Span<T> data) + /* Cast const away, because the implementation for const and non const spans is shared. */ + : VArrayImpl_For_Span<T>({const_cast<T *>(data.data()), data.size()}) + { + } + private: CommonVArrayInfo common_info() const final { @@ -898,10 +904,7 @@ template<typename T> class VArray : public VArrayCommon<T> { VArray(varray_tag::span /* tag */, Span<T> span) { - /* Cast const away, because the virtual array implementation for const and non const spans is - * shared. */ - MutableSpan<T> mutable_span{const_cast<T *>(span.data()), span.size()}; - this->template emplace<VArrayImpl_For_Span_final<T>>(mutable_span); + this->template emplace<VArrayImpl_For_Span_final<T>>(span); } VArray(varray_tag::single /* tag */, T value, const int64_t size) diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index e7ccdeab80a..773aac95193 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -1667,8 +1667,8 @@ bool isect_ray_tri_v3(const float ray_origin[3], float *r_lambda, float r_uv[2]) { - /* NOTE(campbell): these values were 0.000001 in 2.4x but for projection snapping on - * a human head (1BU == 1m), subsurf level 2, this gave many errors. */ + /* NOTE(@campbellbarton): these values were 0.000001 in 2.4x but for projection snapping on + * a human head `(1BU == 1m)`, subdivision-surface level 2, this gave many errors. */ const float epsilon = 0.00000001f; float p[3], s[3], e1[3], e2[3], q[3]; float a, f, u, v; @@ -3773,7 +3773,7 @@ void barycentric_weights_v2_quad(const float v1[2], const float co[2], float w[4]) { - /* NOTE(campbell): fabsf() here is not needed for convex quads + /* NOTE(@campbellbarton): fabsf() here is not needed for convex quads * (and not used in #interp_weights_poly_v2). * But in the case of concave/bow-tie quads for the mask rasterizer it * gives unreliable results without adding `absf()`. If this becomes an issue for more general diff --git a/source/blender/blenlib/intern/math_rotation.c b/source/blender/blenlib/intern/math_rotation.c index 03275ce19b4..ddfaadced60 100644 --- a/source/blender/blenlib/intern/math_rotation.c +++ b/source/blender/blenlib/intern/math_rotation.c @@ -915,107 +915,63 @@ float tri_to_quat(float q[4], const float a[3], const float b[3], const float c[ return len; } -void sin_cos_from_fraction(int numerator, const int denominator, float *r_sin, float *r_cos) +void sin_cos_from_fraction(int numerator, int denominator, float *r_sin, float *r_cos) { - /* By default, creating an circle from an integer: calling #sinf & #cosf on the fraction doesn't - * create symmetrical values (because of float imprecision). + /* By default, creating a circle from an integer: calling #sinf & #cosf on the fraction doesn't + * create symmetrical values (because floats can't represent Pi exactly). * Resolve this when the rotation is calculated from a fraction by mapping the `numerator` * to lower values so X/Y values for points around a circle are exactly symmetrical, see T87779. * - * - Numbers divisible by 4 are mapped to the lower 8th (8 axis symmetry). - * - Even numbers are mapped to the lower quarter (4 axis symmetry). - * - Odd numbers are mapped to the lower half (1 axis symmetry). + * Multiply both the `numerator` and `denominator` by eight to ensure we can divide the circle + * into 8 octants. For each octant, we then use symmetry and negation to bring the `numerator` + * closer to the origin where precision is highest. * - * Once the values are calculated, the are mapped back to their position in the circle - * using negation & swapping values. */ - - BLI_assert((numerator <= denominator) && (denominator > 0)); - enum { NEGATE_SIN_BIT = 0, NEGATE_COS_BIT = 1, SWAP_SIN_COS_BIT = 2 }; - enum { - NEGATE_SIN = (1 << NEGATE_SIN_BIT), - NEGATE_COS = (1 << NEGATE_COS_BIT), - SWAP_SIN_COS = (1 << SWAP_SIN_COS_BIT), - } xform = 0; - if ((denominator & 3) == 0) { - /* The denominator divides by 4, determine the quadrant then further refine the upper 8th. */ - const int denominator_4 = denominator / 4; - if (numerator < denominator_4) { - /* Fall through. */ - } - else { - if (numerator < denominator_4 * 2) { - numerator -= denominator_4; - xform = NEGATE_SIN | SWAP_SIN_COS; - } - else if (numerator == denominator_4 * 2) { - numerator = 0; - xform = NEGATE_COS; - } - else if (numerator < denominator_4 * 3) { - numerator -= denominator_4 * 2; - xform = NEGATE_SIN | NEGATE_COS; - } - else if (numerator == denominator_4 * 3) { - numerator = 0; - xform = NEGATE_COS | SWAP_SIN_COS; - } - else { - numerator -= denominator_4 * 3; - xform = NEGATE_COS | SWAP_SIN_COS; - } - } - /* Further increase accuracy by using the range of the upper 8th. */ - const int numerator_test = denominator_4 - numerator; - if (numerator_test < numerator) { - numerator = numerator_test; - xform ^= SWAP_SIN_COS; - /* Swap #NEGATE_SIN, #NEGATE_COS flags. */ - xform = (xform & (uint)(~(NEGATE_SIN | NEGATE_COS))) | - (((xform & NEGATE_SIN) >> NEGATE_SIN_BIT) << NEGATE_COS_BIT) | - (((xform & NEGATE_COS) >> NEGATE_COS_BIT) << NEGATE_SIN_BIT); - } - } - else if ((denominator & 1) == 0) { - /* The denominator divides by 2, determine the quadrant then further refine the upper 4th. */ - const int denominator_2 = denominator / 2; - if (numerator < denominator_2) { - /* Fall through. */ - } - else if (numerator == denominator_2) { - numerator = 0; - xform = NEGATE_COS; - } - else { - numerator -= denominator_2; - xform = NEGATE_SIN | NEGATE_COS; - } - /* Further increase accuracy by using the range of the upper 4th. */ - const int numerator_test = denominator_2 - numerator; - if (numerator_test < numerator) { - numerator = numerator_test; - xform ^= NEGATE_COS; - } - } - else { - /* The denominator is an odd number, only refine the upper half. */ - const int numerator_test = denominator - numerator; - if (numerator_test < numerator) { - numerator = numerator_test; - xform ^= NEGATE_SIN; - } + * Cases 2, 4, 5 and 7, use the trigonometric identity sin(-x) == -sin(x). + * Cases 1, 2, 5 and 6, swap the pointers `r_sin` and `r_cos`. + */ + BLI_assert(0 <= numerator); + BLI_assert(numerator <= denominator); + BLI_assert(denominator > 0); + + numerator *= 8; /* Multiply numerator the same as denominator. */ + const int octant = numerator / denominator; /* Determine the octant. */ + denominator *= 8; /* Ensure denominator is a multiple of eight. */ + float cos_sign = 1.0f; /* Either 1.0f or -1.0f. */ + + switch (octant) { + case 0: + /* Primary octant, nothing to do. */ + break; + case 1: + case 2: + numerator = (denominator / 4) - numerator; + SWAP(float *, r_sin, r_cos); + break; + case 3: + case 4: + numerator = (denominator / 2) - numerator; + cos_sign = -1.0f; + break; + case 5: + case 6: + numerator = numerator - (denominator * 3 / 4); + SWAP(float *, r_sin, r_cos); + cos_sign = -1.0f; + break; + case 7: + numerator = numerator - denominator; + break; + default: + BLI_assert_unreachable(); } - const float phi = (float)(2.0 * M_PI) * ((float)numerator / (float)denominator); - const float sin_phi = sinf(phi) * ((xform & NEGATE_SIN) ? -1.0f : 1.0f); - const float cos_phi = cosf(phi) * ((xform & NEGATE_COS) ? -1.0f : 1.0f); - if ((xform & SWAP_SIN_COS) == 0) { - *r_sin = sin_phi; - *r_cos = cos_phi; - } - else { - *r_sin = cos_phi; - *r_cos = sin_phi; - } + BLI_assert(-denominator / 4 <= numerator); /* Numerator may be negative. */ + BLI_assert(numerator <= denominator / 4); + BLI_assert(cos_sign == -1.0f || cos_sign == 1.0f); + + const float angle = (float)(2.0 * M_PI) * ((float)numerator / (float)denominator); + *r_sin = sinf(angle); + *r_cos = cosf(angle) * cos_sign; } void print_qt(const char *str, const float q[4]) diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 357dba154af..0d8ad1da582 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -1675,7 +1675,7 @@ static Edge find_good_sorting_edge(const Vert *testp, * The algorithm is similar to the one for find_ambient_cell, except that * instead of an arbitrary point known to be outside the whole mesh, we * have a particular point (v) and we just want to determine the patches - * that that point is between in sorting-around-an-edge order. + * that point is between in sorting-around-an-edge order. */ static int find_containing_cell(const Vert *v, int t, diff --git a/source/blender/blenlib/intern/smallhash.c b/source/blender/blenlib/intern/smallhash.c index 2d76f662611..8263f8ff34e 100644 --- a/source/blender/blenlib/intern/smallhash.c +++ b/source/blender/blenlib/intern/smallhash.c @@ -329,8 +329,7 @@ void **BLI_smallhash_iternew_p(const SmallHash *sh, SmallHashIter *iter, uintptr /** \name Debugging & Introspection * \{ */ -/* NOTE(campbell): this was called _print_smhash in knifetool.c - * it may not be intended for general use. */ +/* NOTE(@campbellbarton): useful for debugging but may not be intended for general use. */ #if 0 void BLI_smallhash_print(SmallHash *sh) { diff --git a/source/blender/blenlib/intern/string_search.cc b/source/blender/blenlib/intern/string_search.cc index 14d85b99739..31ea24eb494 100644 --- a/source/blender/blenlib/intern/string_search.cc +++ b/source/blender/blenlib/intern/string_search.cc @@ -11,8 +11,8 @@ #include "BLI_timeit.hh" /* Right arrow, keep in sync with #UI_MENU_ARROW_SEP in `UI_interface.h`. */ -#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" -#define UI_MENU_ARROW_SEP_UNICODE 0x25b6 +#define UI_MENU_ARROW_SEP "\xe2\x96\xb8" +#define UI_MENU_ARROW_SEP_UNICODE 0x25b8 namespace blender::string_search { diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 0cbf62cce03..93045bd3680 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -403,7 +403,7 @@ int BLI_str_utf8_char_width_safe(const char *p) /* copied from glib's gutf8.c, added 'Err' arg */ -/* NOTE(campbell): glib uses uint for unicode, best we do the same, +/* NOTE(@campbellbarton): glib uses uint for unicode, best we do the same, * though we don't typedef it. */ #define UTF8_COMPUTE(Char, Mask, Len, Err) \ diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc index 5a97b2c7999..9dfa48b5822 100644 --- a/source/blender/blenlib/tests/BLI_set_test.cc +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -532,8 +532,14 @@ TEST(set, ForwardIterator) Set<int>::iterator iter1 = set.begin(); int value1 = *iter1; Set<int>::iterator iter2 = iter1++; - EXPECT_EQ(*iter1, value1); - EXPECT_EQ(*iter2, *(++iter1)); + EXPECT_EQ(*iter2, value1); + EXPECT_EQ(*(++iter2), *iter1); + /* Interesting find: On GCC & MSVC this will succeed, as the 2nd argument is evaluated before the + * 1st. On Apple Clang it's the other way around, and the test fails. */ + // EXPECT_EQ(*iter1, *(++iter1)); + Set<int>::iterator iter3 = ++iter1; + /* Check that #iter1 itself changed. */ + EXPECT_EQ(*iter3, *iter1); } TEST(set, GenericAlgorithms) diff --git a/source/blender/blenlib/tests/BLI_string_search_test.cc b/source/blender/blenlib/tests/BLI_string_search_test.cc index aa6adae8d76..ab1d073ed33 100644 --- a/source/blender/blenlib/tests/BLI_string_search_test.cc +++ b/source/blender/blenlib/tests/BLI_string_search_test.cc @@ -9,7 +9,7 @@ namespace blender::string_search::tests { /* Right arrow, keep in sync with #UI_MENU_ARROW_SEP in `UI_interface.h`. */ -#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" +#define UI_MENU_ARROW_SEP "\xe2\x96\xb8" TEST(string_search, damerau_levenshtein_distance) { diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 5ca026ae9a3..f8bf97b17e9 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -19,6 +19,7 @@ set(INC ../windowmanager ../../../intern/clog ../../../intern/guardedalloc + ../bmesh # for writefile.c: dna_type_offsets.h ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index 9ab744337a8..c79eb2d530b 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -926,7 +926,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { if (md->mode & eModifierMode_Expanded_DEPRECATED) { - md->ui_expand_flag = 1; + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; } else { md->ui_expand_flag = 0; @@ -954,7 +954,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { LISTBASE_FOREACH (bConstraint *, con, &object->constraints) { if (con->flag & CONSTRAINT_EXPAND_DEPRECATED) { - con->ui_expand_flag = 1; + con->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; } else { con->ui_expand_flag = 0; @@ -968,7 +968,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { LISTBASE_FOREACH (GpencilModifierData *, md, &object->greasepencil_modifiers) { if (md->mode & eGpencilModifierMode_Expanded_DEPRECATED) { - md->ui_expand_flag = 1; + md->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; } else { md->ui_expand_flag = 0; @@ -982,7 +982,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { LISTBASE_FOREACH (ShaderFxData *, fx, &object->shader_fx) { if (fx->mode & eShaderFxMode_Expanded_DEPRECATED) { - fx->ui_expand_flag = 1; + fx->ui_expand_flag = UI_PANEL_DATA_EXPAND_ROOT; } else { fx->ui_expand_flag = 0; @@ -1697,7 +1697,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /* Add subpanels for FModifiers, which requires a field to store expansion. */ + /* Add sub-panels for FModifiers, which requires a field to store expansion. */ if (!DNA_struct_elem_find(fd->filesdna, "FModifier", "short", "ui_expand_flag")) { LISTBASE_FOREACH (bAction *, act, &bmain->actions) { LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 49b14dc84a6..b98f8996a2c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -3318,5 +3318,19 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) */ { /* Keep this block, even when empty. */ + + /* Image generation information transferred to tiles. */ + if (!DNA_struct_elem_find(fd->filesdna, "ImageTile", "int", "gen_x")) { + for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { + for (ImageTile *tile = ima->tiles.first; tile; tile = tile->next) { + tile->gen_x = ima->gen_x; + tile->gen_y = ima->gen_y; + tile->gen_type = ima->gen_type; + tile->gen_flag = ima->gen_flag; + tile->gen_depth = ima->gen_depth; + copy_v4_v4(tile->gen_color, ima->gen_color); + } + } + } } } diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index a7637d2712c..0ee5545527b 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -512,16 +512,24 @@ void BM_mesh_copy_init_customdata_from_mesh_array(BMesh *bm_dst, for (int i = 0; i < me_src_array_len; i++) { const Mesh *me_src = me_src_array[i]; if (i == 0) { - CustomData_copy(&me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); - CustomData_copy(&me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); - CustomData_copy(&me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); - CustomData_copy(&me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh( + &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh( + &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh( + &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh( + &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); } else { - CustomData_merge(&me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); - CustomData_merge(&me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); - CustomData_merge(&me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); - CustomData_merge(&me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); + CustomData_merge_mesh_to_bmesh( + &me_src->vdata, &bm_dst->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); + CustomData_merge_mesh_to_bmesh( + &me_src->edata, &bm_dst->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); + CustomData_merge_mesh_to_bmesh( + &me_src->ldata, &bm_dst->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); + CustomData_merge_mesh_to_bmesh( + &me_src->pdata, &bm_dst->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); } cd_flag |= me_src->cd_flag; @@ -714,26 +722,25 @@ BMesh *BM_mesh_copy(BMesh *bm_old) char BM_vert_flag_from_mflag(const char mflag) { - return (((mflag & SELECT) ? BM_ELEM_SELECT : 0) | ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)); + return ((mflag & SELECT) ? BM_ELEM_SELECT : 0); } char BM_edge_flag_from_mflag(const short mflag) { return (((mflag & SELECT) ? BM_ELEM_SELECT : 0) | ((mflag & ME_SEAM) ? BM_ELEM_SEAM : 0) | ((mflag & ME_EDGEDRAW) ? BM_ELEM_DRAW : 0) | - ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0) | /* invert */ - ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)); + ((mflag & ME_SHARP) == 0 ? BM_ELEM_SMOOTH : 0)); } char BM_face_flag_from_mflag(const char mflag) { return (((mflag & ME_FACE_SEL) ? BM_ELEM_SELECT : 0) | - ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0) | ((mflag & ME_HIDE) ? BM_ELEM_HIDDEN : 0)); + ((mflag & ME_SMOOTH) ? BM_ELEM_SMOOTH : 0)); } char BM_vert_flag_to_mflag(BMVert *v) { const char hflag = v->head.hflag; - return (((hflag & BM_ELEM_SELECT) ? SELECT : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0)); + return (((hflag & BM_ELEM_SELECT) ? SELECT : 0)); } short BM_edge_flag_to_mflag(BMEdge *e) @@ -743,7 +750,6 @@ short BM_edge_flag_to_mflag(BMEdge *e) return (((hflag & BM_ELEM_SELECT) ? SELECT : 0) | ((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) | ((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) | ((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) | - ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0) | (BM_edge_is_wire(e) ? ME_LOOSEEDGE : 0) | /* not typical */ ME_EDGERENDER); } @@ -752,5 +758,5 @@ char BM_face_flag_to_mflag(BMFace *f) const char hflag = f->head.hflag; return (((hflag & BM_ELEM_SELECT) ? ME_FACE_SEL : 0) | - ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0) | ((hflag & BM_ELEM_HIDDEN) ? ME_HIDE : 0)); + ((hflag & BM_ELEM_SMOOTH) ? ME_SMOOTH : 0)); } diff --git a/source/blender/bmesh/intern/bmesh_mesh.cc b/source/blender/bmesh/intern/bmesh_mesh.cc index c16d874e3ec..4f42ce4a470 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.cc +++ b/source/blender/bmesh/intern/bmesh_mesh.cc @@ -88,19 +88,19 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm) BMVert_OFlag *v_olfag; BLI_mempool *toolflagpool = bm->vtoolflagpool; BM_ITER_MESH (v_olfag, &iter, bm, BM_VERTS_OF_MESH) { - v_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool); + v_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool)); } BMEdge_OFlag *e_olfag; toolflagpool = bm->etoolflagpool; BM_ITER_MESH (e_olfag, &iter, bm, BM_EDGES_OF_MESH) { - e_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool); + e_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool)); } BMFace_OFlag *f_olfag; toolflagpool = bm->ftoolflagpool; BM_ITER_MESH (f_olfag, &iter, bm, BM_FACES_OF_MESH) { - f_olfag->oflags = (BMFlagLayer *)BLI_mempool_calloc(toolflagpool); + f_olfag->oflags = static_cast<BMFlagLayer *>(BLI_mempool_calloc(toolflagpool)); } bm->totflags = 1; @@ -125,7 +125,7 @@ void BM_mesh_elem_toolflags_clear(BMesh *bm) BMesh *BM_mesh_create(const BMAllocTemplate *allocsize, const struct BMeshCreateParams *params) { /* allocate the structure */ - BMesh *bm = (BMesh *)MEM_callocN(sizeof(BMesh), __func__); + BMesh *bm = static_cast<BMesh *>(MEM_callocN(sizeof(BMesh), __func__)); /* allocate the memory pools for the mesh elements */ bm_mempool_init(bm, allocsize, params->use_toolflags); @@ -262,7 +262,7 @@ void BM_mesh_free(BMesh *bm) if (bm->py_handle) { /* keep this out of 'BM_mesh_data_free' because we want python * to be able to clear the mesh and maintain access. */ - bpy_bm_generic_invalidate((BPy_BMGeneric *)bm->py_handle); + bpy_bm_generic_invalidate(static_cast<BPy_BMGeneric *>(bm->py_handle)); bm->py_handle = nullptr; } @@ -581,7 +581,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype) if (bm->vtable) { MEM_freeN(bm->vtable); } - bm->vtable = (BMVert **)MEM_mallocN(sizeof(void **) * bm->totvert, "bm->vtable"); + bm->vtable = static_cast<BMVert **>( + MEM_mallocN(sizeof(void **) * bm->totvert, "bm->vtable")); bm->vtable_tot = bm->totvert; } BM_iter_as_array(bm, BM_VERTS_OF_MESH, nullptr, (void **)bm->vtable, bm->totvert); @@ -594,7 +595,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype) if (bm->etable) { MEM_freeN(bm->etable); } - bm->etable = (BMEdge **)MEM_mallocN(sizeof(void **) * bm->totedge, "bm->etable"); + bm->etable = static_cast<BMEdge **>( + MEM_mallocN(sizeof(void **) * bm->totedge, "bm->etable")); bm->etable_tot = bm->totedge; } BM_iter_as_array(bm, BM_EDGES_OF_MESH, nullptr, (void **)bm->etable, bm->totedge); @@ -607,7 +609,8 @@ void BM_mesh_elem_table_ensure(BMesh *bm, const char htype) if (bm->ftable) { MEM_freeN(bm->ftable); } - bm->ftable = (BMFace **)MEM_mallocN(sizeof(void **) * bm->totface, "bm->ftable"); + bm->ftable = static_cast<BMFace **>( + MEM_mallocN(sizeof(void **) * bm->totface, "bm->ftable")); bm->ftable_tot = bm->totface; } BM_iter_as_array(bm, BM_FACES_OF_MESH, nullptr, (void **)bm->ftable, bm->totface); @@ -647,17 +650,17 @@ void BM_mesh_elem_table_free(BMesh *bm, const char htype) BMVert *BM_vert_at_index_find(BMesh *bm, const int index) { - return (BMVert *)BLI_mempool_findelem(bm->vpool, index); + return static_cast<BMVert *>(BLI_mempool_findelem(bm->vpool, index)); } BMEdge *BM_edge_at_index_find(BMesh *bm, const int index) { - return (BMEdge *)BLI_mempool_findelem(bm->epool, index); + return static_cast<BMEdge *>(BLI_mempool_findelem(bm->epool, index)); } BMFace *BM_face_at_index_find(BMesh *bm, const int index) { - return (BMFace *)BLI_mempool_findelem(bm->fpool, index); + return static_cast<BMFace *>(BLI_mempool_findelem(bm->fpool, index)); } BMLoop *BM_loop_at_index_find(BMesh *bm, const int index) @@ -754,16 +757,17 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const /* Make a copy of all vertices. */ verts_pool = bm->vtable; - verts_copy = (BMVert *)MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy"); + verts_copy = static_cast<BMVert *>( + MEM_mallocN(sizeof(BMVert) * totvert, "BM_mesh_remap verts copy")); void **pyptrs = (cd_vert_pyptr != -1) ? - (void **)MEM_mallocN(sizeof(void *) * totvert, __func__) : + static_cast<void **>(MEM_mallocN(sizeof(void *) * totvert, __func__)) : nullptr; for (i = totvert, ve = verts_copy + totvert - 1, vep = verts_pool + totvert - 1; i--; ve--, vep--) { *ve = **vep; // printf("*vep: %p, verts_pool[%d]: %p\n", *vep, i, verts_pool[i]); if (cd_vert_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr); + void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)ve), cd_vert_pyptr)); pyptrs[i] = *pyptr; } } @@ -781,7 +785,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const #endif BLI_ghash_insert(vptr_map, *vep, new_vep); if (cd_vert_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr); + void **pyptr = static_cast<void **>( + BM_ELEM_CD_GET_VOID_P(((BMElem *)new_vep), cd_vert_pyptr)); *pyptr = pyptrs[*new_idx]; } } @@ -808,15 +813,16 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const /* Make a copy of all vertices. */ edges_pool = bm->etable; - edges_copy = (BMEdge *)MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy"); + edges_copy = static_cast<BMEdge *>( + MEM_mallocN(sizeof(BMEdge) * totedge, "BM_mesh_remap edges copy")); void **pyptrs = (cd_edge_pyptr != -1) ? - (void **)MEM_mallocN(sizeof(void *) * totedge, __func__) : + static_cast<void **>(MEM_mallocN(sizeof(void *) * totedge, __func__)) : nullptr; for (i = totedge, ed = edges_copy + totedge - 1, edp = edges_pool + totedge - 1; i--; ed--, edp--) { *ed = **edp; if (cd_edge_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr); + void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)ed), cd_edge_pyptr)); pyptrs[i] = *pyptr; } } @@ -834,7 +840,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const "mapping edge from %d to %d (%p/%p to %p)\n", i, *new_idx, *edp, edges_pool[i], new_edp); #endif if (cd_edge_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr); + void **pyptr = static_cast<void **>( + BM_ELEM_CD_GET_VOID_P(((BMElem *)new_edp), cd_edge_pyptr)); *pyptr = pyptrs[*new_idx]; } } @@ -861,15 +868,16 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const /* Make a copy of all vertices. */ faces_pool = bm->ftable; - faces_copy = (BMFace *)MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy"); + faces_copy = static_cast<BMFace *>( + MEM_mallocN(sizeof(BMFace) * totface, "BM_mesh_remap faces copy")); void **pyptrs = (cd_poly_pyptr != -1) ? - (void **)MEM_mallocN(sizeof(void *) * totface, __func__) : + static_cast<void **>(MEM_mallocN(sizeof(void *) * totface, __func__)) : nullptr; for (i = totface, fa = faces_copy + totface - 1, fap = faces_pool + totface - 1; i--; fa--, fap--) { *fa = **fap; if (cd_poly_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr); + void **pyptr = static_cast<void **>(BM_ELEM_CD_GET_VOID_P(((BMElem *)fa), cd_poly_pyptr)); pyptrs[i] = *pyptr; } } @@ -883,7 +891,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const *new_fap = *fa; BLI_ghash_insert(fptr_map, *fap, new_fap); if (cd_poly_pyptr != -1) { - void **pyptr = (void **)BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr); + void **pyptr = static_cast<void **>( + BM_ELEM_CD_GET_VOID_P(((BMElem *)new_fap), cd_poly_pyptr)); *pyptr = pyptrs[*new_idx]; } } @@ -903,7 +912,7 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BM_ITER_MESH (ve, &iter, bm, BM_VERTS_OF_MESH) { // printf("Vert e: %p -> %p\n", ve->e, BLI_ghash_lookup(eptr_map, ve->e)); if (ve->e) { - ve->e = (BMEdge *)BLI_ghash_lookup(eptr_map, ve->e); + ve->e = static_cast<BMEdge *>(BLI_ghash_lookup(eptr_map, ve->e)); BLI_assert(ve->e); } } @@ -919,8 +928,8 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const printf("Edge v1: %p -> %p\n", ed->v1, BLI_ghash_lookup(vptr_map, ed->v1)); printf("Edge v2: %p -> %p\n", ed->v2, BLI_ghash_lookup(vptr_map, ed->v2)); #endif - ed->v1 = (BMVert *)BLI_ghash_lookup(vptr_map, ed->v1); - ed->v2 = (BMVert *)BLI_ghash_lookup(vptr_map, ed->v2); + ed->v1 = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, ed->v1)); + ed->v2 = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, ed->v2)); BLI_assert(ed->v1); BLI_assert(ed->v2); } @@ -939,10 +948,14 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const ed->v2_disk_link.next, BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next)); #endif - ed->v1_disk_link.prev = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev); - ed->v1_disk_link.next = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next); - ed->v2_disk_link.prev = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev); - ed->v2_disk_link.next = (BMEdge *)BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next); + ed->v1_disk_link.prev = static_cast<BMEdge *>( + BLI_ghash_lookup(eptr_map, ed->v1_disk_link.prev)); + ed->v1_disk_link.next = static_cast<BMEdge *>( + BLI_ghash_lookup(eptr_map, ed->v1_disk_link.next)); + ed->v2_disk_link.prev = static_cast<BMEdge *>( + BLI_ghash_lookup(eptr_map, ed->v2_disk_link.prev)); + ed->v2_disk_link.next = static_cast<BMEdge *>( + BLI_ghash_lookup(eptr_map, ed->v2_disk_link.next)); BLI_assert(ed->v1_disk_link.prev); BLI_assert(ed->v1_disk_link.next); BLI_assert(ed->v2_disk_link.prev); @@ -956,17 +969,17 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const BM_ITER_ELEM (lo, &iterl, fa, BM_LOOPS_OF_FACE) { if (vptr_map) { // printf("Loop v: %p -> %p\n", lo->v, BLI_ghash_lookup(vptr_map, lo->v)); - lo->v = (BMVert *)BLI_ghash_lookup(vptr_map, lo->v); + lo->v = static_cast<BMVert *>(BLI_ghash_lookup(vptr_map, lo->v)); BLI_assert(lo->v); } if (eptr_map) { // printf("Loop e: %p -> %p\n", lo->e, BLI_ghash_lookup(eptr_map, lo->e)); - lo->e = (BMEdge *)BLI_ghash_lookup(eptr_map, lo->e); + lo->e = static_cast<BMEdge *>(BLI_ghash_lookup(eptr_map, lo->e)); BLI_assert(lo->e); } if (fptr_map) { // printf("Loop f: %p -> %p\n", lo->f, BLI_ghash_lookup(fptr_map, lo->f)); - lo->f = (BMFace *)BLI_ghash_lookup(fptr_map, lo->f); + lo->f = static_cast<BMFace *>(BLI_ghash_lookup(fptr_map, lo->f)); BLI_assert(lo->f); } } @@ -975,23 +988,23 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const /* Selection history */ { BMEditSelection *ese; - for (ese = (BMEditSelection *)bm->selected.first; ese; ese = ese->next) { + for (ese = static_cast<BMEditSelection *>(bm->selected.first); ese; ese = ese->next) { switch (ese->htype) { case BM_VERT: if (vptr_map) { - ese->ele = (BMElem *)BLI_ghash_lookup(vptr_map, ese->ele); + ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(vptr_map, ese->ele)); BLI_assert(ese->ele); } break; case BM_EDGE: if (eptr_map) { - ese->ele = (BMElem *)BLI_ghash_lookup(eptr_map, ese->ele); + ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(eptr_map, ese->ele)); BLI_assert(ese->ele); } break; case BM_FACE: if (fptr_map) { - ese->ele = (BMElem *)BLI_ghash_lookup(fptr_map, ese->ele); + ese->ele = static_cast<BMElem *>(BLI_ghash_lookup(fptr_map, ese->ele)); BLI_assert(ese->ele); } break; @@ -1001,7 +1014,7 @@ void BM_mesh_remap(BMesh *bm, const uint *vert_idx, const uint *edge_idx, const if (fptr_map) { if (bm->act_face) { - bm->act_face = (BMFace *)BLI_ghash_lookup(fptr_map, bm->act_face); + bm->act_face = static_cast<BMFace *>(BLI_ghash_lookup(fptr_map, bm->act_face)); BLI_assert(bm->act_face); } } @@ -1027,18 +1040,18 @@ void BM_mesh_rebuild(BMesh *bm, const char remap = (vpool_dst ? BM_VERT : 0) | (epool_dst ? BM_EDGE : 0) | (lpool_dst ? BM_LOOP : 0) | (fpool_dst ? BM_FACE : 0); - BMVert **vtable_dst = (remap & BM_VERT) ? - (BMVert **)MEM_mallocN(bm->totvert * sizeof(BMVert *), __func__) : - nullptr; - BMEdge **etable_dst = (remap & BM_EDGE) ? - (BMEdge **)MEM_mallocN(bm->totedge * sizeof(BMEdge *), __func__) : - nullptr; - BMLoop **ltable_dst = (remap & BM_LOOP) ? - (BMLoop **)MEM_mallocN(bm->totloop * sizeof(BMLoop *), __func__) : - nullptr; - BMFace **ftable_dst = (remap & BM_FACE) ? - (BMFace **)MEM_mallocN(bm->totface * sizeof(BMFace *), __func__) : - nullptr; + BMVert **vtable_dst = (remap & BM_VERT) ? static_cast<BMVert **>(MEM_mallocN( + sizeof(BMVert *) * bm->totvert, __func__)) : + nullptr; + BMEdge **etable_dst = (remap & BM_EDGE) ? static_cast<BMEdge **>(MEM_mallocN( + sizeof(BMEdge *) * bm->totedge, __func__)) : + nullptr; + BMLoop **ltable_dst = (remap & BM_LOOP) ? static_cast<BMLoop **>(MEM_mallocN( + sizeof(BMLoop *) * bm->totloop, __func__)) : + nullptr; + BMFace **ftable_dst = (remap & BM_FACE) ? static_cast<BMFace **>(MEM_mallocN( + sizeof(BMFace *) * bm->totface, __func__)) : + nullptr; const bool use_toolflags = params->use_toolflags; @@ -1047,12 +1060,13 @@ void BM_mesh_rebuild(BMesh *bm, int index; BMVert *v_src; BM_ITER_MESH_INDEX (v_src, &iter, bm, BM_VERTS_OF_MESH, index) { - BMVert *v_dst = (BMVert *)BLI_mempool_alloc(vpool_dst); + BMVert *v_dst = static_cast<BMVert *>(BLI_mempool_alloc(vpool_dst)); memcpy(v_dst, v_src, sizeof(BMVert)); if (use_toolflags) { - ((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc( - bm->vtoolflagpool) : - nullptr; + ((BMVert_OFlag *)v_dst)->oflags = bm->vtoolflagpool ? + static_cast<BMFlagLayer *>( + BLI_mempool_calloc(bm->vtoolflagpool)) : + nullptr; } vtable_dst[index] = v_dst; @@ -1065,12 +1079,13 @@ void BM_mesh_rebuild(BMesh *bm, int index; BMEdge *e_src; BM_ITER_MESH_INDEX (e_src, &iter, bm, BM_EDGES_OF_MESH, index) { - BMEdge *e_dst = (BMEdge *)BLI_mempool_alloc(epool_dst); + BMEdge *e_dst = static_cast<BMEdge *>(BLI_mempool_alloc(epool_dst)); memcpy(e_dst, e_src, sizeof(BMEdge)); if (use_toolflags) { - ((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc( - bm->etoolflagpool) : - nullptr; + ((BMEdge_OFlag *)e_dst)->oflags = bm->etoolflagpool ? + static_cast<BMFlagLayer *>( + BLI_mempool_calloc(bm->etoolflagpool)) : + nullptr; } etable_dst[index] = e_dst; @@ -1085,12 +1100,13 @@ void BM_mesh_rebuild(BMesh *bm, BM_ITER_MESH_INDEX (f_src, &iter, bm, BM_FACES_OF_MESH, index) { if (remap & BM_FACE) { - BMFace *f_dst = (BMFace *)BLI_mempool_alloc(fpool_dst); + BMFace *f_dst = static_cast<BMFace *>(BLI_mempool_alloc(fpool_dst)); memcpy(f_dst, f_src, sizeof(BMFace)); if (use_toolflags) { - ((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ? (BMFlagLayer *)BLI_mempool_calloc( - bm->ftoolflagpool) : - nullptr; + ((BMFace_OFlag *)f_dst)->oflags = bm->ftoolflagpool ? + static_cast<BMFlagLayer *>( + BLI_mempool_calloc(bm->ftoolflagpool)) : + nullptr; } ftable_dst[index] = f_dst; @@ -1102,7 +1118,7 @@ void BM_mesh_rebuild(BMesh *bm, BMLoop *l_iter_src, *l_first_src; l_iter_src = l_first_src = BM_FACE_FIRST_LOOP((BMFace *)f_src); do { - BMLoop *l_dst = (BMLoop *)BLI_mempool_alloc(lpool_dst); + BMLoop *l_dst = static_cast<BMLoop *>(BLI_mempool_alloc(lpool_dst)); memcpy(l_dst, l_iter_src, sizeof(BMLoop)); ltable_dst[index_loop] = l_dst; BM_elem_index_set(l_iter_src, index_loop++); /* set_ok */ @@ -1319,7 +1335,8 @@ void BM_mesh_vert_coords_get(BMesh *bm, float (*vert_coords)[3]) float (*BM_mesh_vert_coords_alloc(BMesh *bm, int *r_vert_len))[3] { - float(*vert_coords)[3] = (float(*)[3])MEM_mallocN(bm->totvert * sizeof(*vert_coords), __func__); + float(*vert_coords)[3] = static_cast<float(*)[3]>( + MEM_mallocN(bm->totvert * sizeof(*vert_coords), __func__)); BM_mesh_vert_coords_get(bm, vert_coords); *r_vert_len = bm->totvert; return vert_coords; diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index a5994b52bc2..d766a26cf6e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -17,7 +17,7 @@ void BM_mesh_elem_toolflags_ensure(BMesh *bm); void BM_mesh_elem_toolflags_clear(BMesh *bm); struct BMeshCreateParams { - bool use_toolflags : true; + bool use_toolflags : 1; }; /** diff --git a/source/blender/bmesh/intern/bmesh_mesh_convert.cc b/source/blender/bmesh/intern/bmesh_mesh_convert.cc index 884f6b5388a..b9c004b5392 100644 --- a/source/blender/bmesh/intern/bmesh_mesh_convert.cc +++ b/source/blender/bmesh/intern/bmesh_mesh_convert.cc @@ -83,7 +83,10 @@ #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_span.hh" +#include "BLI_string_ref.hh" +#include "BLI_task.hh" +#include "BKE_attribute.hh" #include "BKE_customdata.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" @@ -103,7 +106,9 @@ static CLG_LogRef LOG = {"bmesh.mesh.convert"}; using blender::Array; using blender::IndexRange; +using blender::MutableSpan; using blender::Span; +using blender::StringRef; void BM_mesh_cd_flag_ensure(BMesh *bm, Mesh *mesh, const char cd_flag) { @@ -212,10 +217,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar if (!me || !me->totvert) { if (me && is_new) { /* No verts? still copy custom-data layout. */ - CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0); - CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0); - CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0); - CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0); + CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_DEFAULT, 0); + CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_DEFAULT, 0); + CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_DEFAULT, 0); + CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_DEFAULT, 0); CustomData_bmesh_init_pool(&bm->vdata, me->totvert, BM_VERT); CustomData_bmesh_init_pool(&bm->edata, me->totedge, BM_EDGE); @@ -231,10 +236,10 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar } if (is_new) { - CustomData_copy(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, 0); - CustomData_copy(&me->edata, &bm->edata, mask.emask, CD_CALLOC, 0); - CustomData_copy(&me->ldata, &bm->ldata, mask.lmask, CD_CALLOC, 0); - CustomData_copy(&me->pdata, &bm->pdata, mask.pmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh(&me->edata, &bm->edata, mask.emask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh(&me->ldata, &bm->ldata, mask.lmask, CD_CALLOC, 0); + CustomData_copy_mesh_to_bmesh(&me->pdata, &bm->pdata, mask.pmask, CD_CALLOC, 0); } else { CustomData_bmesh_merge(&me->vdata, &bm->vdata, mask.vmask, CD_CALLOC, bm, BM_VERT); @@ -352,6 +357,13 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX) : -1; + const bool *hide_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert"); + const bool *hide_edge = (const bool *)CustomData_get_layer_named( + &me->edata, CD_PROP_BOOL, ".hide_edge"); + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &me->pdata, CD_PROP_BOOL, ".hide_poly"); + Span<MVert> mvert{me->mvert, me->totvert}; Array<BMVert *> vtable(me->totvert); for (const int i : mvert.index_range()) { @@ -361,6 +373,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Transfer flag. */ v->head.hflag = BM_vert_flag_from_mflag(mvert[i].flag & ~SELECT); + if (hide_vert && hide_vert[i]) { + BM_elem_flag_enable(v, BM_ELEM_HIDDEN); + } /* This is necessary for selection counts to work properly. */ if (mvert[i].flag & SELECT) { @@ -404,6 +419,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Transfer flags. */ e->head.hflag = BM_edge_flag_from_mflag(medge[i].flag & ~SELECT); + if (hide_edge && hide_edge[i]) { + BM_elem_flag_enable(e, BM_ELEM_HIDDEN); + } /* This is necessary for selection counts to work properly. */ if (medge[i].flag & SELECT) { @@ -457,6 +475,9 @@ void BM_mesh_bm_from_me(BMesh *bm, const Mesh *me, const struct BMeshFromMeshPar /* Transfer flag. */ f->head.hflag = BM_face_flag_from_mflag(mpoly[i].flag & ~ME_FACE_SEL); + if (hide_poly && hide_poly[i]) { + BM_elem_flag_enable(f, BM_ELEM_HIDDEN); + } /* This is necessary for selection counts to work properly. */ if (mpoly[i].flag & ME_FACE_SEL) { @@ -902,6 +923,63 @@ BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e) } } +template<typename GetFn> +static void write_elem_flag_to_attribute(blender::bke::MutableAttributeAccessor &attributes, + const StringRef attribute_name, + const eAttrDomain domain, + const bool do_write, + const GetFn &get_fn) +{ + using namespace blender; + if (do_write) { + bke::SpanAttributeWriter<bool> attribute = attributes.lookup_or_add_for_write_only_span<bool>( + attribute_name, domain); + threading::parallel_for(attribute.span.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + attribute.span[i] = get_fn(i); + } + }); + attribute.finish(); + } + else { + /* To avoid overhead, remove the hide attribute if possible. */ + attributes.remove(attribute_name); + } +} + +static void convert_bmesh_hide_flags_to_mesh_attributes(BMesh &bm, + const bool need_hide_vert, + const bool need_hide_edge, + const bool need_hide_poly, + Mesh &mesh) +{ + using namespace blender; + /* The "hide" attributes are stored as flags on #BMesh. */ + BLI_assert(CustomData_get_layer_named(&bm.vdata, CD_PROP_BOOL, ".hide_vert") == nullptr); + BLI_assert(CustomData_get_layer_named(&bm.edata, CD_PROP_BOOL, ".hide_edge") == nullptr); + BLI_assert(CustomData_get_layer_named(&bm.pdata, CD_PROP_BOOL, ".hide_poly") == nullptr); + + if (!(need_hide_vert || need_hide_edge || need_hide_poly)) { + return; + } + + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(mesh); + BM_mesh_elem_table_ensure(&bm, BM_VERT | BM_EDGE | BM_FACE); + + write_elem_flag_to_attribute( + attributes, ".hide_vert", ATTR_DOMAIN_POINT, need_hide_vert, [&](const int i) { + return BM_elem_flag_test(BM_vert_at_index(&bm, i), BM_ELEM_HIDDEN); + }); + write_elem_flag_to_attribute( + attributes, ".hide_edge", ATTR_DOMAIN_EDGE, need_hide_edge, [&](const int i) { + return BM_elem_flag_test(BM_edge_at_index(&bm, i), BM_ELEM_HIDDEN); + }); + write_elem_flag_to_attribute( + attributes, ".hide_poly", ATTR_DOMAIN_FACE, need_hide_poly, [&](const int i) { + return BM_elem_flag_test(BM_face_at_index(&bm, i), BM_ELEM_HIDDEN); + }); +} + void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMeshParams *params) { MEdge *med; @@ -938,10 +1016,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh { CustomData_MeshMasks mask = CD_MASK_MESH; CustomData_MeshMasks_update(&mask, ¶ms->cd_mask_extra); - CustomData_copy(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert); - CustomData_copy(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge); - CustomData_copy(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop); - CustomData_copy(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly); + CustomData_copy_mesh_to_bmesh(&bm->vdata, &me->vdata, mask.vmask, CD_CALLOC, me->totvert); + CustomData_copy_mesh_to_bmesh(&bm->edata, &me->edata, mask.emask, CD_CALLOC, me->totedge); + CustomData_copy_mesh_to_bmesh(&bm->ldata, &me->ldata, mask.lmask, CD_CALLOC, me->totloop); + CustomData_copy_mesh_to_bmesh(&bm->pdata, &me->pdata, mask.pmask, CD_CALLOC, me->totpoly); } MVert *mvert = bm->totvert ? (MVert *)MEM_callocN(sizeof(MVert) * bm->totvert, "bm_to_me.vert") : @@ -958,6 +1036,10 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh CustomData_add_layer(&me->ldata, CD_MLOOP, CD_ASSIGN, mloop, me->totloop); CustomData_add_layer(&me->pdata, CD_MPOLY, CD_ASSIGN, mpoly, me->totpoly); + bool need_hide_vert = false; + bool need_hide_edge = false; + bool need_hide_poly = false; + /* Clear normals on the mesh completely, since the original vertex and polygon count might be * different than the BMesh's. */ BKE_mesh_clear_derived_normals(me); @@ -972,6 +1054,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh copy_v3_v3(mvert->co, v->co); mvert->flag = BM_vert_flag_to_mflag(v); + if (BM_elem_flag_test(v, BM_ELEM_HIDDEN)) { + need_hide_vert = true; + } BM_elem_index_set(v, i); /* set_inline */ @@ -996,6 +1081,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh med->v2 = BM_elem_index_get(e->v2); med->flag = BM_edge_flag_to_mflag(e); + if (BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + need_hide_edge = true; + } BM_elem_index_set(e, i); /* set_inline */ @@ -1025,6 +1113,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh mpoly->totloop = f->len; mpoly->mat_nr = f->mat_nr; mpoly->flag = BM_face_flag_to_mflag(f); + if (BM_elem_flag_test(f, BM_ELEM_HIDDEN)) { + need_hide_poly = true; + } l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { @@ -1117,6 +1208,9 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh } } + convert_bmesh_hide_flags_to_mesh_attributes( + *bm, need_hide_vert, need_hide_edge, need_hide_poly, *me); + BKE_mesh_update_customdata_pointers(me, false); { @@ -1210,6 +1304,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * const int cd_edge_bweight_offset = CustomData_get_offset(&bm->edata, CD_BWEIGHT); const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE); + bool need_hide_vert = false; + bool need_hide_edge = false; + bool need_hide_poly = false; + /* Clear normals on the mesh completely, since the original vertex and polygon count might be * different than the BMesh's. */ BKE_mesh_clear_derived_normals(me); @@ -1224,6 +1322,13 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * BM_elem_index_set(eve, i); /* set_inline */ mv->flag = BM_vert_flag_to_mflag(eve); + if (BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { + need_hide_vert = true; + } + + if (cd_vert_bweight_offset != -1) { + mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset); + } if (cd_vert_bweight_offset != -1) { mv->bweight = BM_ELEM_CD_GET_FLOAT_AS_UCHAR(eve, cd_vert_bweight_offset); @@ -1242,6 +1347,9 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * med->v2 = BM_elem_index_get(eed->v2); med->flag = BM_edge_flag_to_mflag(eed); + if (BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) { + need_hide_edge = true; + } /* Handle this differently to editmode switching, * only enable draw for single user edges rather than calculating angle. */ @@ -1272,6 +1380,10 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * mp->totloop = efa->len; mp->flag = BM_face_flag_to_mflag(efa); + if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + need_hide_poly = true; + } + mp->loopstart = j; mp->mat_nr = efa->mat_nr; @@ -1291,5 +1403,8 @@ void BM_mesh_bm_to_me_for_eval(BMesh *bm, Mesh *me, const CustomData_MeshMasks * } bm->elem_index_dirty &= ~(BM_FACE | BM_LOOP); + convert_bmesh_hide_flags_to_mesh_attributes( + *bm, need_hide_vert, need_hide_edge, need_hide_poly, *me); + me->cd_flag = BM_mesh_cd_flag_from_bmesh(bm); } diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c index e91dab3dd6f..26f1a9e626e 100644 --- a/source/blender/bmesh/operators/bmo_connect_pair.c +++ b/source/blender/bmesh/operators/bmo_connect_pair.c @@ -83,7 +83,7 @@ typedef struct PathContext { /* only to access BMO flags */ BMesh *bm_bmoflag; - BMVert *v_a, *v_b; + BMVert *v_pair[2]; BLI_mempool *link_pool; } PathContext; @@ -593,17 +593,17 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) } pc.bm_bmoflag = bm; - pc.v_a = ((BMVert **)op_verts_slot->data.p)[0]; - pc.v_b = ((BMVert **)op_verts_slot->data.p)[1]; + pc.v_pair[0] = ((BMVert **)op_verts_slot->data.p)[0]; + pc.v_pair[1] = ((BMVert **)op_verts_slot->data.p)[1]; /* fail! */ - if (!(pc.v_a && pc.v_b)) { + if (!(pc.v_pair[0] && pc.v_pair[1])) { return; } #ifdef DEBUG_PRINT - printf("%s: v_a: %d\n", __func__, BM_elem_index_get(pc.v_a)); - printf("%s: v_b: %d\n", __func__, BM_elem_index_get(pc.v_b)); + printf("%s: v_pair[0]: %d\n", __func__, BM_elem_index_get(pc.v_pair[0])); + printf("%s: v_pair[1]: %d\n", __func__, BM_elem_index_get(pc.v_pair[1])); #endif /* tag so we won't touch ever (typically hidden faces) */ @@ -618,15 +618,15 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) /* calculate matrix */ { - bm_vert_pair_to_matrix(&pc.v_a, pc.matrix); - pc.axis_sep = dot_m3_v3_row_x(pc.matrix, pc.v_a->co); + bm_vert_pair_to_matrix(pc.v_pair, pc.matrix); + pc.axis_sep = dot_m3_v3_row_x(pc.matrix, pc.v_pair[0]->co); } /* add first vertex */ { PathLinkState *state; state = MEM_callocN(sizeof(*state), __func__); - state_link_add(&pc, state, (BMElem *)pc.v_a, NULL); + state_link_add(&pc, state, (BMElem *)pc.v_pair[0], NULL); BLI_heapsimple_insert(pc.states, state->dist, state); } @@ -642,7 +642,7 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) /* either we insert this into 'pc.states' or its freed */ bool continue_search; - if (state->link_last->ele == (BMElem *)pc.v_b) { + if (state->link_last->ele == (BMElem *)pc.v_pair[1]) { /* pass, wait until all are found */ #ifdef DEBUG_PRINT printf("%s: state %p loop found %.4f\n", __func__, state, state->dist); @@ -698,8 +698,8 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) } while ((link = link->next)); } - BMO_vert_flag_enable(bm, pc.v_a, VERT_OUT); - BMO_vert_flag_enable(bm, pc.v_b, VERT_OUT); + BMO_vert_flag_enable(bm, pc.v_pair[0], VERT_OUT); + BMO_vert_flag_enable(bm, pc.v_pair[1], VERT_OUT); BLI_mempool_destroy(pc.link_pool); diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 55e349423bb..f49a9694ab3 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -1,676 +1,682 @@ # SPDX-License-Identifier: GPL-2.0-or-later # Copyright 2011 Blender Foundation. All rights reserved. -set(INC - . - intern - nodes - operations - ../blenkernel - ../blenlib - ../blentranslation - ../depsgraph - ../imbuf - ../makesdna - ../makesrna - ../nodes - ../windowmanager - ../nodes/composite - ../nodes/intern - ../render - ../render/intern - ../../../extern/clew/include - ../../../intern/atomic - ../../../intern/clog - ../../../intern/guardedalloc - - # dna_type_offsets.h - ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern - # RNA_prototypes.h - ${CMAKE_BINARY_DIR}/source/blender/makesrna -) - -set(INC_SYS - -) - -set(SRC - COM_compositor.h - COM_defines.h - - intern/COM_BufferArea.h - intern/COM_BufferOperation.cc - intern/COM_BufferOperation.h - intern/COM_BufferRange.h - intern/COM_BuffersIterator.h - intern/COM_CPUDevice.cc - intern/COM_CPUDevice.h - intern/COM_ChunkOrder.cc - intern/COM_ChunkOrder.h - intern/COM_ChunkOrderHotspot.cc - intern/COM_ChunkOrderHotspot.h - intern/COM_CompositorContext.cc - intern/COM_CompositorContext.h - intern/COM_ConstantFolder.cc - intern/COM_ConstantFolder.h - intern/COM_Converter.cc - intern/COM_Converter.h - intern/COM_Debug.cc - intern/COM_Debug.h - intern/COM_Device.cc - intern/COM_Device.h - intern/COM_Enums.cc - intern/COM_Enums.h - intern/COM_ExecutionGroup.cc - intern/COM_ExecutionGroup.h - intern/COM_ExecutionModel.cc - intern/COM_ExecutionModel.h - intern/COM_ExecutionSystem.cc - intern/COM_ExecutionSystem.h - intern/COM_FullFrameExecutionModel.cc - intern/COM_FullFrameExecutionModel.h - intern/COM_MemoryBuffer.cc - intern/COM_MemoryBuffer.h - intern/COM_MemoryProxy.cc - intern/COM_MemoryProxy.h - intern/COM_MetaData.cc - intern/COM_MetaData.h - intern/COM_MultiThreadedOperation.cc - intern/COM_MultiThreadedOperation.h - intern/COM_MultiThreadedRowOperation.cc - intern/COM_MultiThreadedRowOperation.h - intern/COM_Node.cc - intern/COM_Node.h - intern/COM_NodeConverter.cc - intern/COM_NodeConverter.h - intern/COM_NodeGraph.cc - intern/COM_NodeGraph.h - intern/COM_NodeOperation.cc - intern/COM_NodeOperation.h - intern/COM_NodeOperationBuilder.cc - intern/COM_NodeOperationBuilder.h - intern/COM_OpenCLDevice.cc - intern/COM_OpenCLDevice.h - intern/COM_SharedOperationBuffers.cc - intern/COM_SharedOperationBuffers.h - intern/COM_SingleThreadedOperation.cc - intern/COM_SingleThreadedOperation.h - intern/COM_TiledExecutionModel.cc - intern/COM_TiledExecutionModel.h - intern/COM_WorkPackage.cc - intern/COM_WorkPackage.h - intern/COM_WorkScheduler.cc - intern/COM_WorkScheduler.h - intern/COM_compositor.cc - - operations/COM_QualityStepHelper.cc - operations/COM_QualityStepHelper.h - - # Internal nodes - nodes/COM_SocketProxyNode.cc - nodes/COM_SocketProxyNode.h - - # input nodes - nodes/COM_BokehImageNode.cc - nodes/COM_BokehImageNode.h - nodes/COM_ColorNode.cc - nodes/COM_ColorNode.h - nodes/COM_ImageNode.cc - nodes/COM_ImageNode.h - nodes/COM_MaskNode.cc - nodes/COM_MaskNode.h - nodes/COM_MovieClipNode.cc - nodes/COM_MovieClipNode.h - nodes/COM_OutputFileNode.cc - nodes/COM_OutputFileNode.h - nodes/COM_RenderLayersNode.cc - nodes/COM_RenderLayersNode.h - nodes/COM_SceneTimeNode.cc - nodes/COM_SceneTimeNode.h - nodes/COM_SwitchNode.cc - nodes/COM_SwitchNode.h - nodes/COM_SwitchViewNode.cc - nodes/COM_SwitchViewNode.h - nodes/COM_TextureNode.cc - nodes/COM_TextureNode.h - nodes/COM_TimeNode.cc - nodes/COM_TimeNode.h - nodes/COM_ValueNode.cc - nodes/COM_ValueNode.h - - # output nodes - nodes/COM_CompositorNode.cc - nodes/COM_CompositorNode.h - nodes/COM_SplitViewerNode.cc - nodes/COM_SplitViewerNode.h - nodes/COM_ViewLevelsNode.cc - nodes/COM_ViewLevelsNode.h - nodes/COM_ViewerNode.cc - nodes/COM_ViewerNode.h - operations/COM_CalculateMeanOperation.cc - operations/COM_CalculateMeanOperation.h - operations/COM_CalculateStandardDeviationOperation.cc - operations/COM_CalculateStandardDeviationOperation.h - - # distort nodes - nodes/COM_FlipNode.cc - nodes/COM_FlipNode.h - nodes/COM_RotateNode.cc - nodes/COM_RotateNode.h - nodes/COM_ScaleNode.cc - nodes/COM_ScaleNode.h - nodes/COM_TranslateNode.cc - nodes/COM_TranslateNode.h - - nodes/COM_DisplaceNode.cc - nodes/COM_DisplaceNode.h - nodes/COM_MapUVNode.cc - nodes/COM_MapUVNode.h - - nodes/COM_ChannelMatteNode.cc - nodes/COM_ChannelMatteNode.h - nodes/COM_ChromaMatteNode.cc - nodes/COM_ChromaMatteNode.h - nodes/COM_ColorMatteNode.cc - nodes/COM_ColorMatteNode.h - nodes/COM_DifferenceMatteNode.cc - nodes/COM_DifferenceMatteNode.h - nodes/COM_DistanceMatteNode.cc - nodes/COM_DistanceMatteNode.h - nodes/COM_LensDistortionNode.cc - nodes/COM_LensDistortionNode.h - nodes/COM_LuminanceMatteNode.cc - nodes/COM_LuminanceMatteNode.h - - nodes/COM_GlareNode.cc - nodes/COM_GlareNode.h - - nodes/COM_SunBeamsNode.cc - nodes/COM_SunBeamsNode.h - operations/COM_SunBeamsOperation.cc - operations/COM_SunBeamsOperation.h - - nodes/COM_CryptomatteNode.cc - nodes/COM_CryptomatteNode.h - operations/COM_CryptomatteOperation.cc - operations/COM_CryptomatteOperation.h - - nodes/COM_CornerPinNode.cc - nodes/COM_CornerPinNode.h - nodes/COM_PlaneTrackDeformNode.cc - nodes/COM_PlaneTrackDeformNode.h - - nodes/COM_CropNode.cc - nodes/COM_CropNode.h - operations/COM_CropOperation.cc - operations/COM_CropOperation.h - - nodes/COM_DefocusNode.cc - nodes/COM_DefocusNode.h - nodes/COM_MovieDistortionNode.cc - nodes/COM_MovieDistortionNode.h - nodes/COM_Stabilize2dNode.cc - nodes/COM_Stabilize2dNode.h - nodes/COM_TransformNode.cc - nodes/COM_TransformNode.h - - # color nodes - nodes/COM_AlphaOverNode.cc - nodes/COM_AlphaOverNode.h - nodes/COM_BrightnessNode.cc - nodes/COM_BrightnessNode.h - nodes/COM_ColorBalanceNode.cc - nodes/COM_ColorBalanceNode.h - nodes/COM_ColorCorrectionNode.cc - nodes/COM_ColorCorrectionNode.h - nodes/COM_ColorCurveNode.cc - nodes/COM_ColorCurveNode.h - nodes/COM_ColorExposureNode.cc - nodes/COM_ColorExposureNode.h - nodes/COM_ColorRampNode.cc - nodes/COM_ColorRampNode.h - nodes/COM_ColorToBWNode.cc - nodes/COM_ColorToBWNode.h - nodes/COM_ConvertAlphaNode.cc - nodes/COM_ConvertAlphaNode.h - nodes/COM_ConvertColorSpaceNode.cc - nodes/COM_ConvertColorSpaceNode.h - nodes/COM_GammaNode.cc - nodes/COM_GammaNode.h - nodes/COM_HueSaturationValueCorrectNode.cc - nodes/COM_HueSaturationValueCorrectNode.h - nodes/COM_HueSaturationValueNode.cc - nodes/COM_HueSaturationValueNode.h - nodes/COM_InvertNode.cc - nodes/COM_InvertNode.h - nodes/COM_MixNode.cc - nodes/COM_MixNode.h - nodes/COM_SetAlphaNode.cc - nodes/COM_SetAlphaNode.h - nodes/COM_TonemapNode.cc - nodes/COM_TonemapNode.h - nodes/COM_VectorCurveNode.cc - nodes/COM_VectorCurveNode.h - nodes/COM_ZCombineNode.cc - nodes/COM_ZCombineNode.h - operations/COM_TonemapOperation.cc - operations/COM_TonemapOperation.h - - # 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 - - nodes/COM_MapRangeNode.cc - nodes/COM_MapRangeNode.h - nodes/COM_MapValueNode.cc - nodes/COM_MapValueNode.h - nodes/COM_MathNode.cc - nodes/COM_MathNode.h - nodes/COM_NormalNode.cc - nodes/COM_NormalNode.h - nodes/COM_NormalizeNode.cc - nodes/COM_NormalizeNode.h - - operations/COM_NormalizeOperation.cc - operations/COM_NormalizeOperation.h - - nodes/COM_PixelateNode.cc - nodes/COM_PixelateNode.h - operations/COM_PixelateOperation.cc - operations/COM_PixelateOperation.h - - # Filter nodes - nodes/COM_BilateralBlurNode.cc - nodes/COM_BilateralBlurNode.h - operations/COM_BilateralBlurOperation.cc - operations/COM_BilateralBlurOperation.h - nodes/COM_VectorBlurNode.cc - nodes/COM_VectorBlurNode.h - operations/COM_VectorBlurOperation.cc - operations/COM_VectorBlurOperation.h - nodes/COM_AntiAliasingNode.cc - nodes/COM_AntiAliasingNode.h - nodes/COM_BlurNode.cc - nodes/COM_BlurNode.h - nodes/COM_BokehBlurNode.cc - nodes/COM_BokehBlurNode.h - nodes/COM_DenoiseNode.cc - nodes/COM_DenoiseNode.h - nodes/COM_DespeckleNode.cc - nodes/COM_DespeckleNode.h - nodes/COM_DilateErodeNode.cc - nodes/COM_DilateErodeNode.h - nodes/COM_DirectionalBlurNode.cc - nodes/COM_DirectionalBlurNode.h - nodes/COM_FilterNode.cc - nodes/COM_FilterNode.h - nodes/COM_InpaintNode.cc - nodes/COM_InpaintNode.h - nodes/COM_PosterizeNode.cc - nodes/COM_PosterizeNode.h - - operations/COM_BlurBaseOperation.cc - operations/COM_BlurBaseOperation.h - operations/COM_BokehBlurOperation.cc - operations/COM_BokehBlurOperation.h - operations/COM_DirectionalBlurOperation.cc - operations/COM_DirectionalBlurOperation.h - operations/COM_FastGaussianBlurOperation.cc - operations/COM_FastGaussianBlurOperation.h - operations/COM_GammaCorrectOperation.cc - operations/COM_GammaCorrectOperation.h - operations/COM_GaussianAlphaBlurBaseOperation.cc - operations/COM_GaussianAlphaBlurBaseOperation.h - operations/COM_GaussianAlphaXBlurOperation.cc - operations/COM_GaussianAlphaXBlurOperation.h - operations/COM_GaussianAlphaYBlurOperation.cc - operations/COM_GaussianAlphaYBlurOperation.h - operations/COM_GaussianBlurBaseOperation.cc - operations/COM_GaussianBlurBaseOperation.h - operations/COM_GaussianBokehBlurOperation.cc - operations/COM_GaussianBokehBlurOperation.h - operations/COM_GaussianXBlurOperation.cc - operations/COM_GaussianXBlurOperation.h - operations/COM_GaussianYBlurOperation.cc - operations/COM_GaussianYBlurOperation.h - operations/COM_MovieClipAttributeOperation.cc - operations/COM_MovieClipAttributeOperation.h - operations/COM_MovieDistortionOperation.cc - operations/COM_MovieDistortionOperation.h - operations/COM_PosterizeOperation.cc - operations/COM_PosterizeOperation.h - operations/COM_SMAAOperation.cc - operations/COM_SMAAOperation.h - operations/COM_VariableSizeBokehBlurOperation.cc - operations/COM_VariableSizeBokehBlurOperation.h - - # Matte nodes - nodes/COM_BoxMaskNode.cc - nodes/COM_BoxMaskNode.h - nodes/COM_ColorSpillNode.cc - nodes/COM_ColorSpillNode.h - nodes/COM_DoubleEdgeMaskNode.cc - nodes/COM_DoubleEdgeMaskNode.h - nodes/COM_EllipseMaskNode.cc - nodes/COM_EllipseMaskNode.h - - operations/COM_DoubleEdgeMaskOperation.cc - operations/COM_DoubleEdgeMaskOperation.h - - - nodes/COM_KeyingScreenNode.cc - nodes/COM_KeyingScreenNode.h - operations/COM_KeyingScreenOperation.cc - operations/COM_KeyingScreenOperation.h - - nodes/COM_TrackPositionNode.cc - nodes/COM_TrackPositionNode.h - operations/COM_TrackPositionOperation.cc - operations/COM_TrackPositionOperation.h - - nodes/COM_KeyingNode.cc - nodes/COM_KeyingNode.h - operations/COM_KeyingBlurOperation.cc - operations/COM_KeyingBlurOperation.h - operations/COM_KeyingClipOperation.cc - operations/COM_KeyingClipOperation.h - operations/COM_KeyingDespillOperation.cc - operations/COM_KeyingDespillOperation.h - operations/COM_KeyingOperation.cc - operations/COM_KeyingOperation.h - - operations/COM_ColorSpillOperation.cc - operations/COM_ColorSpillOperation.h - operations/COM_RenderLayersProg.cc - operations/COM_RenderLayersProg.h - - operations/COM_BokehImageOperation.cc - operations/COM_BokehImageOperation.h - operations/COM_ImageOperation.cc - operations/COM_ImageOperation.h - operations/COM_MultilayerImageOperation.cc - operations/COM_MultilayerImageOperation.h - operations/COM_TextureOperation.cc - operations/COM_TextureOperation.h - - - operations/COM_SocketProxyOperation.cc - operations/COM_SocketProxyOperation.h - - operations/COM_CompositorOperation.cc - operations/COM_CompositorOperation.h - operations/COM_ConvertDepthToRadiusOperation.cc - operations/COM_ConvertDepthToRadiusOperation.h - operations/COM_OutputFileMultiViewOperation.cc - operations/COM_OutputFileMultiViewOperation.h - operations/COM_OutputFileOperation.cc - operations/COM_OutputFileOperation.h - operations/COM_PreviewOperation.cc - operations/COM_PreviewOperation.h - operations/COM_SplitOperation.cc - operations/COM_SplitOperation.h - operations/COM_ViewerOperation.cc - operations/COM_ViewerOperation.h - operations/COM_ZCombineOperation.cc - operations/COM_ZCombineOperation.h - - operations/COM_ChangeHSVOperation.cc - operations/COM_ChangeHSVOperation.h - operations/COM_ChannelMatteOperation.cc - operations/COM_ChannelMatteOperation.h - operations/COM_ChromaMatteOperation.cc - operations/COM_ChromaMatteOperation.h - operations/COM_ColorCurveOperation.cc - operations/COM_ColorCurveOperation.h - operations/COM_ColorExposureOperation.cc - operations/COM_ColorExposureOperation.h - operations/COM_ColorMatteOperation.cc - operations/COM_ColorMatteOperation.h - operations/COM_ColorRampOperation.cc - operations/COM_ColorRampOperation.h - operations/COM_CurveBaseOperation.cc - operations/COM_CurveBaseOperation.h - operations/COM_DifferenceMatteOperation.cc - operations/COM_DifferenceMatteOperation.h - operations/COM_DistanceRGBMatteOperation.cc - operations/COM_DistanceRGBMatteOperation.h - operations/COM_DistanceYCCMatteOperation.cc - operations/COM_DistanceYCCMatteOperation.h - operations/COM_HueSaturationValueCorrectOperation.cc - operations/COM_HueSaturationValueCorrectOperation.h - operations/COM_LuminanceMatteOperation.cc - operations/COM_LuminanceMatteOperation.h - operations/COM_VectorCurveOperation.cc - operations/COM_VectorCurveOperation.h - - operations/COM_BrightnessOperation.cc - operations/COM_BrightnessOperation.h - operations/COM_ColorCorrectionOperation.cc - operations/COM_ColorCorrectionOperation.h - operations/COM_ConstantOperation.cc - operations/COM_ConstantOperation.h - operations/COM_GammaOperation.cc - operations/COM_GammaOperation.h - operations/COM_MixOperation.cc - operations/COM_MixOperation.h - operations/COM_ReadBufferOperation.cc - operations/COM_ReadBufferOperation.h - operations/COM_SetColorOperation.cc - operations/COM_SetColorOperation.h - operations/COM_SetValueOperation.cc - operations/COM_SetValueOperation.h - operations/COM_SetVectorOperation.cc - operations/COM_SetVectorOperation.h - operations/COM_WriteBufferOperation.cc - operations/COM_WriteBufferOperation.h - - operations/COM_MathBaseOperation.cc - operations/COM_MathBaseOperation.h - - operations/COM_AlphaOverKeyOperation.cc - operations/COM_AlphaOverKeyOperation.h - operations/COM_AlphaOverMixedOperation.cc - operations/COM_AlphaOverMixedOperation.h - operations/COM_AlphaOverPremultiplyOperation.cc - operations/COM_AlphaOverPremultiplyOperation.h - - operations/COM_ColorBalanceASCCDLOperation.cc - operations/COM_ColorBalanceASCCDLOperation.h - operations/COM_ColorBalanceLGGOperation.cc - operations/COM_ColorBalanceLGGOperation.h - operations/COM_InvertOperation.cc - operations/COM_InvertOperation.h - operations/COM_MapRangeOperation.cc - operations/COM_MapRangeOperation.h - operations/COM_MapValueOperation.cc - operations/COM_MapValueOperation.h - operations/COM_SetAlphaMultiplyOperation.cc - operations/COM_SetAlphaMultiplyOperation.h - operations/COM_SetAlphaReplaceOperation.cc - operations/COM_SetAlphaReplaceOperation.h - - # Distort operation - operations/COM_DisplaceOperation.cc - operations/COM_DisplaceOperation.h - operations/COM_DisplaceSimpleOperation.cc - operations/COM_DisplaceSimpleOperation.h - operations/COM_FlipOperation.cc - operations/COM_FlipOperation.h - operations/COM_MapUVOperation.cc - operations/COM_MapUVOperation.h - operations/COM_PlaneCornerPinOperation.cc - operations/COM_PlaneCornerPinOperation.h - operations/COM_PlaneDistortCommonOperation.cc - operations/COM_PlaneDistortCommonOperation.h - operations/COM_PlaneTrackOperation.cc - operations/COM_PlaneTrackOperation.h - operations/COM_ProjectorLensDistortionOperation.cc - operations/COM_ProjectorLensDistortionOperation.h - operations/COM_RotateOperation.cc - operations/COM_RotateOperation.h - operations/COM_ScaleOperation.cc - operations/COM_ScaleOperation.h - operations/COM_ScreenLensDistortionOperation.cc - operations/COM_ScreenLensDistortionOperation.h - operations/COM_TransformOperation.cc - operations/COM_TransformOperation.h - operations/COM_TranslateOperation.cc - operations/COM_TranslateOperation.h - operations/COM_WrapOperation.cc - operations/COM_WrapOperation.h - - # Filter operations - operations/COM_ConvolutionEdgeFilterOperation.cc - operations/COM_ConvolutionEdgeFilterOperation.h - operations/COM_ConvolutionFilterOperation.cc - operations/COM_ConvolutionFilterOperation.h - operations/COM_DenoiseOperation.cc - operations/COM_DenoiseOperation.h - operations/COM_DespeckleOperation.cc - operations/COM_DespeckleOperation.h - operations/COM_DilateErodeOperation.cc - operations/COM_DilateErodeOperation.h - operations/COM_GlareBaseOperation.cc - operations/COM_GlareBaseOperation.h - operations/COM_GlareFogGlowOperation.cc - operations/COM_GlareFogGlowOperation.h - operations/COM_GlareGhostOperation.cc - operations/COM_GlareGhostOperation.h - operations/COM_GlareSimpleStarOperation.cc - operations/COM_GlareSimpleStarOperation.h - operations/COM_GlareStreaksOperation.cc - operations/COM_GlareStreaksOperation.h - operations/COM_GlareThresholdOperation.cc - operations/COM_GlareThresholdOperation.h - operations/COM_InpaintOperation.cc - operations/COM_InpaintOperation.h - operations/COM_SetSamplerOperation.cc - operations/COM_SetSamplerOperation.h - - - # Convert operations - operations/COM_ConvertOperation.cc - operations/COM_ConvertOperation.h - operations/COM_IDMaskOperation.cc - operations/COM_IDMaskOperation.h - - operations/COM_ConvertColorSpaceOperation.cc - operations/COM_ConvertColorSpaceOperation.h - operations/COM_DotproductOperation.cc - operations/COM_DotproductOperation.h - - # Matte operation - operations/COM_BoxMaskOperation.cc - operations/COM_BoxMaskOperation.h - operations/COM_EllipseMaskOperation.cc - operations/COM_EllipseMaskOperation.h - - operations/COM_ConvertColorProfileOperation.cc - operations/COM_ConvertColorProfileOperation.h - operations/COM_MovieClipOperation.cc - operations/COM_MovieClipOperation.h - - operations/COM_AntiAliasOperation.cc - operations/COM_AntiAliasOperation.h - - operations/COM_MaskOperation.cc - operations/COM_MaskOperation.h -) - -set(LIB - bf_blenkernel - bf_blenlib - extern_clew -) - -list(APPEND INC - ${CMAKE_CURRENT_BINARY_DIR}/operations -) - -data_to_c( - ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl - ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h - SRC -) - -add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) - -set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) -set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) -add_custom_command( - OUTPUT ${GENSRC} - COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} - COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} - DEPENDS smaa_areatex -) -add_custom_target(smaa_areatex_header - SOURCES ${GENSRC} -) -list(APPEND SRC - ${GENSRC} -) -unset(GENSRC) -unset(GENSRC_DIR) - -if(WITH_OPENIMAGEDENOISE) - add_definitions(-DWITH_OPENIMAGEDENOISE) - add_definitions(-DOIDN_STATIC_LIB) - list(APPEND INC_SYS - ${OPENIMAGEDENOISE_INCLUDE_DIRS} - ${TBB_INCLUDE_DIRS} +add_subdirectory(realtime_compositor) + +if(WITH_COMPOSITOR_CPU) + set(INC + . + intern + nodes + operations + ../blenkernel + ../blenlib + ../blentranslation + ../depsgraph + ../imbuf + ../makesdna + ../makesrna + ../nodes + ../windowmanager + ../nodes/composite + ../nodes/intern + ../render + ../render/intern + ../../../extern/clew/include + ../../../intern/atomic + ../../../intern/clog + ../../../intern/guardedalloc + + # dna_type_offsets.h + ${CMAKE_CURRENT_BINARY_DIR}/../makesdna/intern + # RNA_prototypes.h + ${CMAKE_BINARY_DIR}/source/blender/makesrna ) - list(APPEND LIB - ${OPENIMAGEDENOISE_LIBRARIES} - ${TBB_LIBRARIES} + + set(INC_SYS + ) -endif() -blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + set(SRC + COM_compositor.h + COM_defines.h + + intern/COM_BufferArea.h + intern/COM_BufferOperation.cc + intern/COM_BufferOperation.h + intern/COM_BufferRange.h + intern/COM_BuffersIterator.h + intern/COM_CPUDevice.cc + intern/COM_CPUDevice.h + intern/COM_ChunkOrder.cc + intern/COM_ChunkOrder.h + intern/COM_ChunkOrderHotspot.cc + intern/COM_ChunkOrderHotspot.h + intern/COM_CompositorContext.cc + intern/COM_CompositorContext.h + intern/COM_ConstantFolder.cc + intern/COM_ConstantFolder.h + intern/COM_Converter.cc + intern/COM_Converter.h + intern/COM_Debug.cc + intern/COM_Debug.h + intern/COM_Device.cc + intern/COM_Device.h + intern/COM_Enums.cc + intern/COM_Enums.h + intern/COM_ExecutionGroup.cc + intern/COM_ExecutionGroup.h + intern/COM_ExecutionModel.cc + intern/COM_ExecutionModel.h + intern/COM_ExecutionSystem.cc + intern/COM_ExecutionSystem.h + intern/COM_FullFrameExecutionModel.cc + intern/COM_FullFrameExecutionModel.h + intern/COM_MemoryBuffer.cc + intern/COM_MemoryBuffer.h + intern/COM_MemoryProxy.cc + intern/COM_MemoryProxy.h + intern/COM_MetaData.cc + intern/COM_MetaData.h + intern/COM_MultiThreadedOperation.cc + intern/COM_MultiThreadedOperation.h + intern/COM_MultiThreadedRowOperation.cc + intern/COM_MultiThreadedRowOperation.h + intern/COM_Node.cc + intern/COM_Node.h + intern/COM_NodeConverter.cc + intern/COM_NodeConverter.h + intern/COM_NodeGraph.cc + intern/COM_NodeGraph.h + intern/COM_NodeOperation.cc + intern/COM_NodeOperation.h + intern/COM_NodeOperationBuilder.cc + intern/COM_NodeOperationBuilder.h + intern/COM_OpenCLDevice.cc + intern/COM_OpenCLDevice.h + intern/COM_SharedOperationBuffers.cc + intern/COM_SharedOperationBuffers.h + intern/COM_SingleThreadedOperation.cc + intern/COM_SingleThreadedOperation.h + intern/COM_TiledExecutionModel.cc + intern/COM_TiledExecutionModel.h + intern/COM_WorkPackage.cc + intern/COM_WorkPackage.h + intern/COM_WorkScheduler.cc + intern/COM_WorkScheduler.h + intern/COM_compositor.cc + + operations/COM_QualityStepHelper.cc + operations/COM_QualityStepHelper.h + + # Internal nodes + nodes/COM_SocketProxyNode.cc + nodes/COM_SocketProxyNode.h + + # input nodes + nodes/COM_BokehImageNode.cc + nodes/COM_BokehImageNode.h + nodes/COM_ColorNode.cc + nodes/COM_ColorNode.h + nodes/COM_ImageNode.cc + nodes/COM_ImageNode.h + nodes/COM_MaskNode.cc + nodes/COM_MaskNode.h + nodes/COM_MovieClipNode.cc + nodes/COM_MovieClipNode.h + nodes/COM_OutputFileNode.cc + nodes/COM_OutputFileNode.h + nodes/COM_RenderLayersNode.cc + nodes/COM_RenderLayersNode.h + nodes/COM_SceneTimeNode.cc + nodes/COM_SceneTimeNode.h + nodes/COM_SwitchNode.cc + nodes/COM_SwitchNode.h + nodes/COM_SwitchViewNode.cc + nodes/COM_SwitchViewNode.h + nodes/COM_TextureNode.cc + nodes/COM_TextureNode.h + nodes/COM_TimeNode.cc + nodes/COM_TimeNode.h + nodes/COM_ValueNode.cc + nodes/COM_ValueNode.h + + # output nodes + nodes/COM_CompositorNode.cc + nodes/COM_CompositorNode.h + nodes/COM_SplitViewerNode.cc + nodes/COM_SplitViewerNode.h + nodes/COM_ViewLevelsNode.cc + nodes/COM_ViewLevelsNode.h + nodes/COM_ViewerNode.cc + nodes/COM_ViewerNode.h + operations/COM_CalculateMeanOperation.cc + operations/COM_CalculateMeanOperation.h + operations/COM_CalculateStandardDeviationOperation.cc + operations/COM_CalculateStandardDeviationOperation.h + + # distort nodes + nodes/COM_FlipNode.cc + nodes/COM_FlipNode.h + nodes/COM_RotateNode.cc + nodes/COM_RotateNode.h + nodes/COM_ScaleNode.cc + nodes/COM_ScaleNode.h + nodes/COM_TranslateNode.cc + nodes/COM_TranslateNode.h + + nodes/COM_DisplaceNode.cc + nodes/COM_DisplaceNode.h + nodes/COM_MapUVNode.cc + nodes/COM_MapUVNode.h + + nodes/COM_ChannelMatteNode.cc + nodes/COM_ChannelMatteNode.h + nodes/COM_ChromaMatteNode.cc + nodes/COM_ChromaMatteNode.h + nodes/COM_ColorMatteNode.cc + nodes/COM_ColorMatteNode.h + nodes/COM_DifferenceMatteNode.cc + nodes/COM_DifferenceMatteNode.h + nodes/COM_DistanceMatteNode.cc + nodes/COM_DistanceMatteNode.h + nodes/COM_LensDistortionNode.cc + nodes/COM_LensDistortionNode.h + nodes/COM_LuminanceMatteNode.cc + nodes/COM_LuminanceMatteNode.h + + nodes/COM_GlareNode.cc + nodes/COM_GlareNode.h + + nodes/COM_SunBeamsNode.cc + nodes/COM_SunBeamsNode.h + operations/COM_SunBeamsOperation.cc + operations/COM_SunBeamsOperation.h + + nodes/COM_CryptomatteNode.cc + nodes/COM_CryptomatteNode.h + operations/COM_CryptomatteOperation.cc + operations/COM_CryptomatteOperation.h + + nodes/COM_CornerPinNode.cc + nodes/COM_CornerPinNode.h + nodes/COM_PlaneTrackDeformNode.cc + nodes/COM_PlaneTrackDeformNode.h + + nodes/COM_CropNode.cc + nodes/COM_CropNode.h + operations/COM_CropOperation.cc + operations/COM_CropOperation.h + + nodes/COM_DefocusNode.cc + nodes/COM_DefocusNode.h + nodes/COM_MovieDistortionNode.cc + nodes/COM_MovieDistortionNode.h + nodes/COM_Stabilize2dNode.cc + nodes/COM_Stabilize2dNode.h + nodes/COM_TransformNode.cc + nodes/COM_TransformNode.h + + # color nodes + nodes/COM_AlphaOverNode.cc + nodes/COM_AlphaOverNode.h + nodes/COM_BrightnessNode.cc + nodes/COM_BrightnessNode.h + nodes/COM_ColorBalanceNode.cc + nodes/COM_ColorBalanceNode.h + nodes/COM_ColorCorrectionNode.cc + nodes/COM_ColorCorrectionNode.h + nodes/COM_ColorCurveNode.cc + nodes/COM_ColorCurveNode.h + nodes/COM_ColorExposureNode.cc + nodes/COM_ColorExposureNode.h + nodes/COM_ColorRampNode.cc + nodes/COM_ColorRampNode.h + nodes/COM_ColorToBWNode.cc + nodes/COM_ColorToBWNode.h + nodes/COM_ConvertAlphaNode.cc + nodes/COM_ConvertAlphaNode.h + nodes/COM_ConvertColorSpaceNode.cc + nodes/COM_ConvertColorSpaceNode.h + nodes/COM_GammaNode.cc + nodes/COM_GammaNode.h + nodes/COM_HueSaturationValueCorrectNode.cc + nodes/COM_HueSaturationValueCorrectNode.h + nodes/COM_HueSaturationValueNode.cc + nodes/COM_HueSaturationValueNode.h + nodes/COM_InvertNode.cc + nodes/COM_InvertNode.h + nodes/COM_MixNode.cc + nodes/COM_MixNode.h + nodes/COM_SetAlphaNode.cc + nodes/COM_SetAlphaNode.h + nodes/COM_TonemapNode.cc + nodes/COM_TonemapNode.h + nodes/COM_VectorCurveNode.cc + nodes/COM_VectorCurveNode.h + nodes/COM_ZCombineNode.cc + nodes/COM_ZCombineNode.h + operations/COM_TonemapOperation.cc + operations/COM_TonemapOperation.h + + # 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 + + nodes/COM_MapRangeNode.cc + nodes/COM_MapRangeNode.h + nodes/COM_MapValueNode.cc + nodes/COM_MapValueNode.h + nodes/COM_MathNode.cc + nodes/COM_MathNode.h + nodes/COM_NormalNode.cc + nodes/COM_NormalNode.h + nodes/COM_NormalizeNode.cc + nodes/COM_NormalizeNode.h + + operations/COM_NormalizeOperation.cc + operations/COM_NormalizeOperation.h + + nodes/COM_PixelateNode.cc + nodes/COM_PixelateNode.h + operations/COM_PixelateOperation.cc + operations/COM_PixelateOperation.h + + # Filter nodes + nodes/COM_BilateralBlurNode.cc + nodes/COM_BilateralBlurNode.h + operations/COM_BilateralBlurOperation.cc + operations/COM_BilateralBlurOperation.h + nodes/COM_VectorBlurNode.cc + nodes/COM_VectorBlurNode.h + operations/COM_VectorBlurOperation.cc + operations/COM_VectorBlurOperation.h + nodes/COM_AntiAliasingNode.cc + nodes/COM_AntiAliasingNode.h + nodes/COM_BlurNode.cc + nodes/COM_BlurNode.h + nodes/COM_BokehBlurNode.cc + nodes/COM_BokehBlurNode.h + nodes/COM_DenoiseNode.cc + nodes/COM_DenoiseNode.h + nodes/COM_DespeckleNode.cc + nodes/COM_DespeckleNode.h + nodes/COM_DilateErodeNode.cc + nodes/COM_DilateErodeNode.h + nodes/COM_DirectionalBlurNode.cc + nodes/COM_DirectionalBlurNode.h + nodes/COM_FilterNode.cc + nodes/COM_FilterNode.h + nodes/COM_InpaintNode.cc + nodes/COM_InpaintNode.h + nodes/COM_PosterizeNode.cc + nodes/COM_PosterizeNode.h + + operations/COM_BlurBaseOperation.cc + operations/COM_BlurBaseOperation.h + operations/COM_BokehBlurOperation.cc + operations/COM_BokehBlurOperation.h + operations/COM_DirectionalBlurOperation.cc + operations/COM_DirectionalBlurOperation.h + operations/COM_FastGaussianBlurOperation.cc + operations/COM_FastGaussianBlurOperation.h + operations/COM_GammaCorrectOperation.cc + operations/COM_GammaCorrectOperation.h + operations/COM_GaussianAlphaBlurBaseOperation.cc + operations/COM_GaussianAlphaBlurBaseOperation.h + operations/COM_GaussianAlphaXBlurOperation.cc + operations/COM_GaussianAlphaXBlurOperation.h + operations/COM_GaussianAlphaYBlurOperation.cc + operations/COM_GaussianAlphaYBlurOperation.h + operations/COM_GaussianBlurBaseOperation.cc + operations/COM_GaussianBlurBaseOperation.h + operations/COM_GaussianBokehBlurOperation.cc + operations/COM_GaussianBokehBlurOperation.h + operations/COM_GaussianXBlurOperation.cc + operations/COM_GaussianXBlurOperation.h + operations/COM_GaussianYBlurOperation.cc + operations/COM_GaussianYBlurOperation.h + operations/COM_MovieClipAttributeOperation.cc + operations/COM_MovieClipAttributeOperation.h + operations/COM_MovieDistortionOperation.cc + operations/COM_MovieDistortionOperation.h + operations/COM_PosterizeOperation.cc + operations/COM_PosterizeOperation.h + operations/COM_SMAAOperation.cc + operations/COM_SMAAOperation.h + operations/COM_VariableSizeBokehBlurOperation.cc + operations/COM_VariableSizeBokehBlurOperation.h + + # Matte nodes + nodes/COM_BoxMaskNode.cc + nodes/COM_BoxMaskNode.h + nodes/COM_ColorSpillNode.cc + nodes/COM_ColorSpillNode.h + nodes/COM_DoubleEdgeMaskNode.cc + nodes/COM_DoubleEdgeMaskNode.h + nodes/COM_EllipseMaskNode.cc + nodes/COM_EllipseMaskNode.h + + operations/COM_DoubleEdgeMaskOperation.cc + operations/COM_DoubleEdgeMaskOperation.h + + + nodes/COM_KeyingScreenNode.cc + nodes/COM_KeyingScreenNode.h + operations/COM_KeyingScreenOperation.cc + operations/COM_KeyingScreenOperation.h + + nodes/COM_TrackPositionNode.cc + nodes/COM_TrackPositionNode.h + operations/COM_TrackPositionOperation.cc + operations/COM_TrackPositionOperation.h + + nodes/COM_KeyingNode.cc + nodes/COM_KeyingNode.h + operations/COM_KeyingBlurOperation.cc + operations/COM_KeyingBlurOperation.h + operations/COM_KeyingClipOperation.cc + operations/COM_KeyingClipOperation.h + operations/COM_KeyingDespillOperation.cc + operations/COM_KeyingDespillOperation.h + operations/COM_KeyingOperation.cc + operations/COM_KeyingOperation.h + + operations/COM_ColorSpillOperation.cc + operations/COM_ColorSpillOperation.h + operations/COM_RenderLayersProg.cc + operations/COM_RenderLayersProg.h + + operations/COM_BokehImageOperation.cc + operations/COM_BokehImageOperation.h + operations/COM_ImageOperation.cc + operations/COM_ImageOperation.h + operations/COM_MultilayerImageOperation.cc + operations/COM_MultilayerImageOperation.h + operations/COM_TextureOperation.cc + operations/COM_TextureOperation.h + + + operations/COM_SocketProxyOperation.cc + operations/COM_SocketProxyOperation.h + + operations/COM_CompositorOperation.cc + operations/COM_CompositorOperation.h + operations/COM_ConvertDepthToRadiusOperation.cc + operations/COM_ConvertDepthToRadiusOperation.h + operations/COM_OutputFileMultiViewOperation.cc + operations/COM_OutputFileMultiViewOperation.h + operations/COM_OutputFileOperation.cc + operations/COM_OutputFileOperation.h + operations/COM_PreviewOperation.cc + operations/COM_PreviewOperation.h + operations/COM_SplitOperation.cc + operations/COM_SplitOperation.h + operations/COM_ViewerOperation.cc + operations/COM_ViewerOperation.h + operations/COM_ZCombineOperation.cc + operations/COM_ZCombineOperation.h + + operations/COM_ChangeHSVOperation.cc + operations/COM_ChangeHSVOperation.h + operations/COM_ChannelMatteOperation.cc + operations/COM_ChannelMatteOperation.h + operations/COM_ChromaMatteOperation.cc + operations/COM_ChromaMatteOperation.h + operations/COM_ColorCurveOperation.cc + operations/COM_ColorCurveOperation.h + operations/COM_ColorExposureOperation.cc + operations/COM_ColorExposureOperation.h + operations/COM_ColorMatteOperation.cc + operations/COM_ColorMatteOperation.h + operations/COM_ColorRampOperation.cc + operations/COM_ColorRampOperation.h + operations/COM_CurveBaseOperation.cc + operations/COM_CurveBaseOperation.h + operations/COM_DifferenceMatteOperation.cc + operations/COM_DifferenceMatteOperation.h + operations/COM_DistanceRGBMatteOperation.cc + operations/COM_DistanceRGBMatteOperation.h + operations/COM_DistanceYCCMatteOperation.cc + operations/COM_DistanceYCCMatteOperation.h + operations/COM_HueSaturationValueCorrectOperation.cc + operations/COM_HueSaturationValueCorrectOperation.h + operations/COM_LuminanceMatteOperation.cc + operations/COM_LuminanceMatteOperation.h + operations/COM_VectorCurveOperation.cc + operations/COM_VectorCurveOperation.h + + operations/COM_BrightnessOperation.cc + operations/COM_BrightnessOperation.h + operations/COM_ColorCorrectionOperation.cc + operations/COM_ColorCorrectionOperation.h + operations/COM_ConstantOperation.cc + operations/COM_ConstantOperation.h + operations/COM_GammaOperation.cc + operations/COM_GammaOperation.h + operations/COM_MixOperation.cc + operations/COM_MixOperation.h + operations/COM_ReadBufferOperation.cc + operations/COM_ReadBufferOperation.h + operations/COM_SetColorOperation.cc + operations/COM_SetColorOperation.h + operations/COM_SetValueOperation.cc + operations/COM_SetValueOperation.h + operations/COM_SetVectorOperation.cc + operations/COM_SetVectorOperation.h + operations/COM_WriteBufferOperation.cc + operations/COM_WriteBufferOperation.h + + operations/COM_MathBaseOperation.cc + operations/COM_MathBaseOperation.h + + operations/COM_AlphaOverKeyOperation.cc + operations/COM_AlphaOverKeyOperation.h + operations/COM_AlphaOverMixedOperation.cc + operations/COM_AlphaOverMixedOperation.h + operations/COM_AlphaOverPremultiplyOperation.cc + operations/COM_AlphaOverPremultiplyOperation.h + + operations/COM_ColorBalanceASCCDLOperation.cc + operations/COM_ColorBalanceASCCDLOperation.h + operations/COM_ColorBalanceLGGOperation.cc + operations/COM_ColorBalanceLGGOperation.h + operations/COM_InvertOperation.cc + operations/COM_InvertOperation.h + operations/COM_MapRangeOperation.cc + operations/COM_MapRangeOperation.h + operations/COM_MapValueOperation.cc + operations/COM_MapValueOperation.h + operations/COM_SetAlphaMultiplyOperation.cc + operations/COM_SetAlphaMultiplyOperation.h + operations/COM_SetAlphaReplaceOperation.cc + operations/COM_SetAlphaReplaceOperation.h + + # Distort operation + operations/COM_DisplaceOperation.cc + operations/COM_DisplaceOperation.h + operations/COM_DisplaceSimpleOperation.cc + operations/COM_DisplaceSimpleOperation.h + operations/COM_FlipOperation.cc + operations/COM_FlipOperation.h + operations/COM_MapUVOperation.cc + operations/COM_MapUVOperation.h + operations/COM_PlaneCornerPinOperation.cc + operations/COM_PlaneCornerPinOperation.h + operations/COM_PlaneDistortCommonOperation.cc + operations/COM_PlaneDistortCommonOperation.h + operations/COM_PlaneTrackOperation.cc + operations/COM_PlaneTrackOperation.h + operations/COM_ProjectorLensDistortionOperation.cc + operations/COM_ProjectorLensDistortionOperation.h + operations/COM_RotateOperation.cc + operations/COM_RotateOperation.h + operations/COM_ScaleOperation.cc + operations/COM_ScaleOperation.h + operations/COM_ScreenLensDistortionOperation.cc + operations/COM_ScreenLensDistortionOperation.h + operations/COM_TransformOperation.cc + operations/COM_TransformOperation.h + operations/COM_TranslateOperation.cc + operations/COM_TranslateOperation.h + operations/COM_WrapOperation.cc + operations/COM_WrapOperation.h + + # Filter operations + operations/COM_ConvolutionEdgeFilterOperation.cc + operations/COM_ConvolutionEdgeFilterOperation.h + operations/COM_ConvolutionFilterOperation.cc + operations/COM_ConvolutionFilterOperation.h + operations/COM_DenoiseOperation.cc + operations/COM_DenoiseOperation.h + operations/COM_DespeckleOperation.cc + operations/COM_DespeckleOperation.h + operations/COM_DilateErodeOperation.cc + operations/COM_DilateErodeOperation.h + operations/COM_GlareBaseOperation.cc + operations/COM_GlareBaseOperation.h + operations/COM_GlareFogGlowOperation.cc + operations/COM_GlareFogGlowOperation.h + operations/COM_GlareGhostOperation.cc + operations/COM_GlareGhostOperation.h + operations/COM_GlareSimpleStarOperation.cc + operations/COM_GlareSimpleStarOperation.h + operations/COM_GlareStreaksOperation.cc + operations/COM_GlareStreaksOperation.h + operations/COM_GlareThresholdOperation.cc + operations/COM_GlareThresholdOperation.h + operations/COM_InpaintOperation.cc + operations/COM_InpaintOperation.h + operations/COM_SetSamplerOperation.cc + operations/COM_SetSamplerOperation.h + + + # Convert operations + operations/COM_ConvertOperation.cc + operations/COM_ConvertOperation.h + operations/COM_IDMaskOperation.cc + operations/COM_IDMaskOperation.h + + operations/COM_ConvertColorSpaceOperation.cc + operations/COM_ConvertColorSpaceOperation.h + operations/COM_DotproductOperation.cc + operations/COM_DotproductOperation.h + + # Matte operation + operations/COM_BoxMaskOperation.cc + operations/COM_BoxMaskOperation.h + operations/COM_EllipseMaskOperation.cc + operations/COM_EllipseMaskOperation.h + + operations/COM_ConvertColorProfileOperation.cc + operations/COM_ConvertColorProfileOperation.h + operations/COM_MovieClipOperation.cc + operations/COM_MovieClipOperation.h + + operations/COM_AntiAliasOperation.cc + operations/COM_AntiAliasOperation.h + + operations/COM_MaskOperation.cc + operations/COM_MaskOperation.h + ) -if(WITH_UNITY_BUILD) - set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON) - set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10) -endif() + set(LIB + bf_blenkernel + bf_blenlib + extern_clew + ) -if(COMMAND target_precompile_headers) - target_precompile_headers(bf_compositor PRIVATE COM_precomp.h) -endif() + list(APPEND INC + ${CMAKE_CURRENT_BINARY_DIR}/operations + ) -if(CXX_WARN_NO_SUGGEST_OVERRIDE) - target_compile_options(bf_compositor PRIVATE "-Wsuggest-override") -endif() + data_to_c( + ${CMAKE_CURRENT_SOURCE_DIR}/operations/COM_OpenCLKernels.cl + ${CMAKE_CURRENT_BINARY_DIR}/operations/COM_OpenCLKernels.cl.h + SRC + ) -add_dependencies(bf_compositor smaa_areatex_header) + add_definitions(-DCL_USE_DEPRECATED_OPENCL_1_1_APIS) -if(WITH_GTESTS) - set(TEST_SRC - tests/COM_BufferArea_test.cc - tests/COM_BufferRange_test.cc - tests/COM_BuffersIterator_test.cc - tests/COM_NodeOperation_test.cc + set(GENSRC_DIR ${CMAKE_CURRENT_BINARY_DIR}/operations) + set(GENSRC ${GENSRC_DIR}/COM_SMAAAreaTexture.h) + add_custom_command( + OUTPUT ${GENSRC} + COMMAND ${CMAKE_COMMAND} -E make_directory ${GENSRC_DIR} + COMMAND "$<TARGET_FILE:smaa_areatex>" ${GENSRC} + DEPENDS smaa_areatex ) - set(TEST_INC + add_custom_target(smaa_areatex_header + SOURCES ${GENSRC} ) - set(TEST_LIB - bf_compositor + list(APPEND SRC + ${GENSRC} ) - include(GTestTesting) - blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") -endif() + unset(GENSRC) + unset(GENSRC_DIR) + + if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) + add_definitions(-DOIDN_STATIC_LIB) + list(APPEND INC_SYS + ${OPENIMAGEDENOISE_INCLUDE_DIRS} + ${TBB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENIMAGEDENOISE_LIBRARIES} + ${TBB_LIBRARIES} + ) + endif() + + blender_add_lib(bf_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + + if(WITH_UNITY_BUILD) + set_target_properties(bf_compositor PROPERTIES UNITY_BUILD ON) + set_target_properties(bf_compositor PROPERTIES UNITY_BUILD_BATCH_SIZE 10) + endif() + + if(COMMAND target_precompile_headers) + target_precompile_headers(bf_compositor PRIVATE COM_precomp.h) + endif() + + if(CXX_WARN_NO_SUGGEST_OVERRIDE) + target_compile_options(bf_compositor PRIVATE "-Wsuggest-override") + endif() + + add_dependencies(bf_compositor smaa_areatex_header) + + if(WITH_GTESTS) + set(TEST_SRC + tests/COM_BufferArea_test.cc + tests/COM_BufferRange_test.cc + tests/COM_BuffersIterator_test.cc + tests/COM_NodeOperation_test.cc + ) + set(TEST_INC + ) + set(TEST_LIB + bf_compositor + ) + include(GTestTesting) + blender_add_test_lib(bf_compositor_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") + endif() + + # Needed so we can use dna_type_offsets.h for defaults initialization. + add_dependencies(bf_compositor bf_dna) + # RNA_prototypes.h + add_dependencies(bf_compositor bf_rna) -# Needed so we can use dna_type_offsets.h for defaults initialization. -add_dependencies(bf_compositor bf_dna) -# RNA_prototypes.h -add_dependencies(bf_compositor bf_rna) +# End WITH_COMPOSITOR_CPU. +endif() diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index d7cf41cf422..b62d972e807 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -74,7 +74,7 @@ void MovieClipBaseOperation::execute_pixel_sampled(float output[4], zero_v4(output); } else if (ibuf->rect == nullptr && ibuf->rect_float == nullptr) { - /* Happens for multilayer exr, i.e. */ + /* Happens for multi-layer EXR, i.e. */ zero_v4(output); } else { diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 1957c5eb5fc..2a2aff31893 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -7,7 +7,7 @@ namespace blender::compositor { #define USE_FORCE_BILINEAR -/* XXX(campbell): ignore input and use default from old compositor, +/* XXX(@campbellbarton): ignore input and use default from old compositor, * could become an option like the transform node. * * NOTE: use bilinear because bicubic makes fuzzy even when not scaling at all (1:1) diff --git a/source/blender/compositor/realtime_compositor/CMakeLists.txt b/source/blender/compositor/realtime_compositor/CMakeLists.txt new file mode 100644 index 00000000000..9fe156c3ef2 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/CMakeLists.txt @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +set(INC + . + ../../gpu + ../../nodes + ../../imbuf + ../../blenlib + ../../makesdna + ../../makesrna + ../../blenkernel + ../../gpu/intern + ../../../../intern/guardedalloc +) + + +set(SRC + intern/compile_state.cc + intern/context.cc + intern/conversion_operation.cc + intern/domain.cc + intern/evaluator.cc + intern/input_single_value_operation.cc + intern/node_operation.cc + intern/operation.cc + intern/realize_on_domain_operation.cc + intern/reduce_to_single_value_operation.cc + intern/result.cc + intern/scheduler.cc + intern/shader_node.cc + intern/shader_operation.cc + intern/simple_operation.cc + intern/static_shader_manager.cc + intern/texture_pool.cc + intern/utilities.cc + + COM_compile_state.hh + COM_context.hh + COM_conversion_operation.hh + COM_domain.hh + COM_evaluator.hh + COM_input_descriptor.hh + COM_input_single_value_operation.hh + COM_node_operation.hh + COM_operation.hh + COM_realize_on_domain_operation.hh + COM_reduce_to_single_value_operation.hh + COM_result.hh + COM_scheduler.hh + COM_shader_node.hh + COM_shader_operation.hh + COM_simple_operation.hh + COM_static_shader_manager.hh + COM_texture_pool.hh + COM_utilities.hh +) + +set(LIB + bf_gpu + bf_nodes + bf_imbuf + bf_blenlib + bf_blenkernel +) + +blender_add_lib(bf_realtime_compositor "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/compositor/realtime_compositor/COM_compile_state.hh b/source/blender/compositor/realtime_compositor/COM_compile_state.hh new file mode 100644 index 00000000000..ed6ad414e3b --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_compile_state.hh @@ -0,0 +1,170 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_map.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_domain.hh" +#include "COM_node_operation.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Compile State + * + * The compile state is a utility class used to track the state of compilation when compiling the + * node tree. In particular, it tracks two important pieces of information, each of which is + * described in one of the following sections. + * + * First, it stores a mapping between all nodes and the operations they were compiled into. The + * mapping are stored independently depending on the type of the operation in the node_operations_ + * and shader_operations_ maps. So those two maps are mutually exclusive. The compiler should call + * the map_node_to_node_operation and map_node_to_shader_operation methods to populate those maps + * as soon as it compiles a node or multiple nodes into an operation. Those maps are used to + * retrieve the results of outputs linked to the inputs of operations. For more details, see the + * get_result_from_output_socket method. For the node tree shown below, nodes 1, 2, and 6 are + * mapped to their compiled operations in the node_operation_ map. While nodes 3 and 4 are both + * mapped to the first shader operation, and node 5 is mapped to the second shader operation in the + * shader_operations_ map. + * + * Shader Operation 1 Shader Operation 2 + * +-----------------------------------+ +------------------+ + * .------------. | .------------. .------------. | | .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 | + * | |----|--| |--| |---|-----|--| |--|--| | + * | | .-|--| | | | | .--|--| | | | | + * '------------' | | '------------' '------------' | | | '------------' | '------------' + * | +-----------------------------------+ | +------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'----------------------------------------' + * | | + * '------------' + * + * Second, it stores the shader compile unit as well as its domain. One should first go over the + * discussion in COM_evaluator.hh for a high level description of the mechanism of the compile + * unit. The one important detail in this class is the should_compile_shader_compile_unit method, + * which implements the criteria of whether the compile unit should be compiled given the node + * currently being processed as an argument. Those criteria are described as follows. If the + * compile unit is empty as is the case when processing nodes 1, 2, and 3, then it plainly + * shouldn't be compiled. If the given node is not a shader node, then it can't be added to the + * compile unit and the unit is considered complete and should be compiled, as is the case when + * processing node 6. If the computed domain of the given node is not compatible with the domain of + * the compiled unit, then it can't be added to the unit and the unit is considered complete and + * should be compiled, as is the case when processing node 5, more on this in the next section. + * Otherwise, the given node is compatible with the compile unit and can be added to it, so the + * unit shouldn't be compiled just yet, as is the case when processing node 4. + * + * Special attention should be given to the aforementioned domain compatibility criterion. One + * should first go over the discussion in COM_domain.hh for more information on domains. When a + * compile unit gets eventually compiled to a shader operation, that operation will have a certain + * operation domain, and any node that gets added to the compile unit should itself have a computed + * node domain that is compatible with that operation domain, otherwise, had the node been compiled + * into its own operation separately, the result would have been be different. For instance, + * consider the above node tree where node 1 outputs a 100x100 result, node 2 outputs a 50x50 + * result, the first input in node 3 has the highest domain priority, and the second input in node + * 5 has the highest domain priority. In this case, shader operation 1 will output a 100x100 + * result, and shader operation 2 will output a 50x50 result, because that's the computed operation + * domain for each of them. So node 6 will get a 50x50 result. Now consider the same node tree, but + * where all three nodes 3, 4, and 5 were compiled into a single shader operation as shown the node + * tree below. In that case, shader operation 1 will output a 100x100 result, because that's its + * computed operation domain. So node 6 will get a 100x100 result. As can be seen, the final result + * is different even though the node tree is the same. That's why the compiler can decide to + * compile the compile unit early even though further nodes can still be technically added to it. + * + * Shader Operation 1 + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * To check for the domain compatibility between the compile unit and the node being processed, + * the domain of the compile unit is assumed to be the domain of the first node whose computed + * domain is not an identity domain. Identity domains corresponds to single value results, so those + * are always compatible with any domain. The domain of the compile unit is computed and set in + * the add_node_to_shader_compile_unit method. When processing a node, the computed domain of node + * is compared to the compile unit domain in the should_compile_shader_compile_unit method, noting + * that identity domains are always compatible. Node domains are computed in the + * compute_shader_node_domain method, which is analogous to Operation::compute_domain for nodes + * that are not yet compiled. */ +class CompileState { + private: + /* A reference to the node execution schedule that is being compiled. */ + const Schedule &schedule_; + /* Those two maps associate each node with the operation it was compiled into. Each node is + * either compiled into a node operation and added to node_operations, or compiled into a shader + * operation and added to shader_operations. Those maps are used to retrieve the results of + * outputs linked to the inputs of operations. See the get_result_from_output_socket method for + * more information. */ + Map<DNode, NodeOperation *> node_operations_; + Map<DNode, ShaderOperation *> shader_operations_; + /* A contiguous subset of the node execution schedule that contains the group of nodes that will + * be compiled together into a Shader Operation. See the discussion in COM_evaluator.hh for + * more information. */ + ShaderCompileUnit shader_compile_unit_; + /* The domain of the shader compile unit. */ + Domain shader_compile_unit_domain_ = Domain::identity(); + + public: + /* Construct a compile state from the node execution schedule being compiled. */ + CompileState(const Schedule &schedule); + + /* Get a reference to the node execution schedule being compiled. */ + const Schedule &get_schedule(); + + /* Add an association between the given node and the give node operation that the node was + * compiled into in the node_operations_ map. */ + void map_node_to_node_operation(DNode node, NodeOperation *operation); + + /* Add an association between the given node and the give shader operation that the node was + * compiled into in the shader_operations_ map. */ + void map_node_to_shader_operation(DNode node, ShaderOperation *operation); + + /* Returns a reference to the result of the operation corresponding to the given output that the + * given output's node was compiled to. */ + Result &get_result_from_output_socket(DOutputSocket output); + + /* Add the given node to the compile unit. And if the domain of the compile unit is not yet + * determined or was determined to be an identity domain, update it to the computed domain for + * the give node. */ + void add_node_to_shader_compile_unit(DNode node); + + /* Get a reference to the shader compile unit. */ + ShaderCompileUnit &get_shader_compile_unit(); + + /* Clear the compile unit. This should be called once the compile unit is compiled to ready it to + * track the next potential compile unit. */ + void reset_shader_compile_unit(); + + /* Determines if the compile unit should be compiled based on a number of criteria give the node + * currently being processed. Those criteria are as follows: + * - If compile unit is empty, then it can't and shouldn't be compiled. + * - If the given node is not a shader node, then it can't be added to the compile unit + * and the unit is considered complete and should be compiled. + * - If the computed domain of the given node is not compatible with the domain of the compile + * unit, then it can't be added to it and the unit is considered complete and should be + * compiled. */ + bool should_compile_shader_compile_unit(DNode node); + + private: + /* Compute the node domain of the given shader node. This is analogous to the + * Operation::compute_domain method, except it is computed from the node itself as opposed to a + * compiled operation. See the discussion in COM_domain.hh for more information. */ + Domain compute_shader_node_domain(DNode node); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_context.hh b/source/blender/compositor/realtime_compositor/COM_context.hh new file mode 100644 index 00000000000..b5c8cea641f --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_context.hh @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" + +#include "DNA_scene_types.h" + +#include "GPU_texture.h" + +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Context + * + * A Context is an abstract class that is implemented by the caller of the evaluator to provide the + * necessary data and functionalities for the correct operation of the evaluator. This includes + * providing input data like render passes and the active scene, as well as references to the data + * where the output of the evaluator will be written. The class also provides a reference to the + * texture pool which should be implemented by the caller and provided during construction. + * Finally, the class have an instance of a static shader manager for convenient shader + * acquisition. */ +class Context { + private: + /* A texture pool that can be used to allocate textures for the compositor efficiently. */ + TexturePool &texture_pool_; + /* A static shader manager that can be used to acquire shaders for the compositor efficiently. */ + StaticShaderManager shader_manager_; + + public: + Context(TexturePool &texture_pool); + + /* Get the active compositing scene. */ + virtual const Scene *get_scene() const = 0; + + /* Get the dimensions of the output. */ + virtual int2 get_output_size() = 0; + + /* Get the texture representing the output where the result of the compositor should be + * written. This should be called by output nodes to get their target texture. */ + virtual GPUTexture *get_output_texture() = 0; + + /* Get the texture where the given render pass is stored. This should be called by the Render + * Layer node to populate its outputs. */ + virtual GPUTexture *get_input_texture(int view_layer, eScenePassType pass_type) = 0; + + /* Get the name of the view currently being rendered. */ + virtual StringRef get_view_name() = 0; + + /* Set an info message. This is called by the compositor evaluator to inform or warn the user + * about something, typically an error. The implementation should display the message in an + * appropriate place, which can be directly in the UI or just logged to the output stream. */ + virtual void set_info_message(StringRef message) const = 0; + + /* Get the current frame number of the active scene. */ + int get_frame_number() const; + + /* Get the current time in seconds of the active scene. */ + float get_time() const; + + /* Get a reference to the texture pool of this context. */ + TexturePool &texture_pool(); + + /* Get a reference to the static shader manager of this context. */ + StaticShaderManager &shader_manager(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh new file mode 100644 index 00000000000..15e1d0722ea --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_conversion_operation.hh @@ -0,0 +1,126 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Conversion Operation + * + * A simple operation that converts a result from a certain type to another. See the derived + * classes for more details. */ +class ConversionOperation : public SimpleOperation { + public: + using SimpleOperation::SimpleOperation; + + /* If the input result is a single value, execute_single is called. Otherwise, the shader + * provided by get_conversion_shader is dispatched. */ + void execute() override; + + /* Determine if a conversion operation is needed for the input with the given result and + * descriptor. If it is not needed, return a null pointer. If it is needed, return an instance of + * the appropriate conversion operation. */ + static SimpleOperation *construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor); + + protected: + /* Convert the input single value result to the output single value result. */ + virtual void execute_single(const Result &input, Result &output) = 0; + + /* Get the shader the will be used for conversion. */ + virtual GPUShader *get_conversion_shader() const = 0; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Float To Vector Operation + * + * Takes a float result and outputs a vector result. All three components of the output are filled + * with the input float. */ +class ConvertFloatToVectorOperation : public ConversionOperation { + public: + ConvertFloatToVectorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Float To Color Operation + * + * Takes a float result and outputs a color result. All three color channels of the output are + * filled with the input float and the alpha channel is set to 1. */ +class ConvertFloatToColorOperation : public ConversionOperation { + public: + ConvertFloatToColorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Color To Float Operation + * + * Takes a color result and outputs a float result. The output is the average of the three color + * channels, the alpha channel is ignored. */ +class ConvertColorToFloatOperation : public ConversionOperation { + public: + ConvertColorToFloatOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Color To Vector Operation + * + * Takes a color result and outputs a vector result. The output is a copy of the three color + * channels to the three vector components. */ +class ConvertColorToVectorOperation : public ConversionOperation { + public: + ConvertColorToVectorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Vector To Float Operation + * + * Takes a vector result and outputs a float result. The output is the average of the three + * components. */ +class ConvertVectorToFloatOperation : public ConversionOperation { + public: + ConvertVectorToFloatOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +/* ------------------------------------------------------------------------------------------------- + * Convert Vector To Color Operation + * + * Takes a vector result and outputs a color result. The output is a copy of the three vector + * components to the three color channels with the alpha channel set to 1. */ +class ConvertVectorToColorOperation : public ConversionOperation { + public: + ConvertVectorToColorOperation(Context &context); + + void execute_single(const Result &input, Result &output) override; + + GPUShader *get_conversion_shader() const override; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_domain.hh b/source/blender/compositor/realtime_compositor/COM_domain.hh new file mode 100644 index 00000000000..a4f9eb68db4 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_domain.hh @@ -0,0 +1,166 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +namespace blender::realtime_compositor { + +/* Possible interpolations to use when realizing an input result of some domain on another domain. + * See the RealizationOptions struct for more information. */ +enum class Interpolation : uint8_t { + Nearest, + Bilinear, + Bicubic, +}; + +/* ------------------------------------------------------------------------------------------------ + * Realization Options + * + * The options that describe how an input result prefer to be realized on some other domain. This + * is used by the Realize On Domain Operation to identify the appropriate method of realization. + * See the Domain class for more information. */ +struct RealizationOptions { + /* The interpolation method that should be used when performing realization. Since realizing a + * result involves projecting it on a different domain, which in turn, involves sampling the + * result at arbitrary locations, the interpolation identifies the method used for computing the + * value at those arbitrary locations. */ + Interpolation interpolation = Interpolation::Nearest; + /* If true, the result will be repeated infinitely along the horizontal axis when realizing the + * result. If false, regions outside of bounds of the result along the horizontal axis will be + * filled with zeros. */ + bool repeat_x = false; + /* If true, the result will be repeated infinitely along the vertical axis when realizing the + * result. If false, regions outside of bounds of the result along the vertical axis will be + * filled with zeros. */ + bool repeat_y = false; +}; + +/* ------------------------------------------------------------------------------------------------ + * Domain + * + * The compositor is designed in such a way as to allow compositing in an infinite virtual + * compositing space. Consequently, any result of an operation is not only represented by its image + * output, but also by its transformation in that virtual space. The transformation of the result + * together with the dimension of its image is stored and represented by a Domain. In the figure + * below, two results of different domains are illustrated on the virtual compositing space. One of + * the results is centered in space with an image dimension of 800px × 600px, and the other result + * is scaled down and translated such that it lies in the upper right quadrant of the space with an + * image dimension of 800px × 400px. The position of the domain is in pixel space, and the domain + * is considered centered if it has an identity transformation. Note that both results have the + * same resolution, but occupy different areas of the virtual compositing space. + * + * y + * ^ + * 800px x 600px | + * .---------------------|---------------------. + * | | 800px x 600px | + * | | .-------------. | + * | | | | | + * | | '-------------' | + * ------|---------------------|---------------------|------> x + * | | | + * | | | + * | | | + * | | | + * '---------------------|---------------------' + * | + * + * By default, results have domains of identity transformations, that is, they are centered in + * space, but a transformation operation like the rotate, translate, or transform operations will + * adjust the transformation to make the result reside somewhere different in space. The domain of + * a single value result is irrelevant and always set to an identity domain. + * + * An operation is typically only concerned about a subset of the virtual compositing space, this + * subset is represented by a domain which is called the Operation Domain. It follows that before + * the operation itself is executed, inputs will typically be realized on the operation domain to + * be in the same domain and have the same dimension as that of the operation domain. This process + * is called Domain Realization and is implemented using an operation called the Realize On Domain + * Operation. Realization involves projecting the result onto the target domain, copying the area + * of the result that intersects the target domain, and filling the rest with zeros or repetitions + * of the result depending on the realization options that can be set by the user. Consequently, + * operations can generally expect their inputs to have the same dimension and can operate on them + * directly and transparently. For instance, if an operation takes both results illustrated in + * the figure above, and the operation has an operation domain that matches the bigger domain, the + * result with the bigger domain will not need to be realized because it already has a domain that + * matches that of the operation domain, but the result with the smaller domain will have to be + * realized into a new result that has the same domain as the domain of the bigger result. Assuming + * no repetition, the output of the realization will be an all zeros image with dimension 800px × + * 600px with a small scaled version of the smaller result copied into the upper right quadrant of + * the image. The following figure illustrates the realization process on a different set of + * results + * + * Realized Result + * +-------------+ +-------------+ + * | Operation | | | + * | Domain | | Zeros | + * | | ----> | | + * +-----|-----+ | |-----+ | + * | | C | | | C | | + * | +-----|-------+ +-----'-------+ + * | Domain Of | + * | Input | + * +-----------+ + * + * An operation can operate in an arbitrary operation domain, but in most cases, the operation + * domain is inferred from the inputs of the operation. In particular, one of the inputs is said to + * be the Domain Input of the operation and the operation domain is inferred from its domain. It + * follows that this particular input will not need realization, because it already has the correct + * domain. The domain input selection mechanism is as follows. Each of the inputs are assigned a + * value by the developer called the Domain Priority, the domain input is then chosen as the + * non-single value input with the highest domain priority, zero being the highest priority. See + * Operation::compute_domain for more information. + * + * The aforementioned logic for operation domain computation is only a default that works for most + * cases, but an operation can override the compute_domain method to implement a different logic. + * For instance, output nodes have an operation domain the same size as the viewport and with an + * identity transformation, their operation domain doesn't depend on the inputs at all. + * + * For instance, a filter operation has two inputs, a factor and a color, the latter of which is + * assigned a domain priority of 0 and the former is assigned a domain priority of 1. If the color + * input is not a single value input, then the color input is considered to be the domain input of + * the operation and the operation domain is computed to be the same domain as the color input, + * because it has the highest priority. It follows that if the factor input has a different domain + * than the computed domain of the operation, it will be projected and realized on it to have the + * same domain as described above. On the other hand, if the color input is a single value input, + * then the factor input is considered to be the domain input and the operation domain will be the + * same as the domain of the factor input, because it has the second highest domain priority. + * Finally, if both inputs are single value inputs, the operation domain will be an identity domain + * and is irrelevant, because the output will be a domain-less single value. */ +class Domain { + public: + /* The size of the domain in pixels. */ + int2 size; + /* The 2D transformation of the domain defining its translation in pixels, rotation, and scale in + * the virtual compositing space. */ + float3x3 transformation; + /* The options that describe how this domain prefer to be realized on some other domain. See the + * RealizationOptions struct for more information. */ + RealizationOptions realization_options; + + public: + /* A size only constructor that sets the transformation to identity. */ + Domain(int2 size); + + Domain(int2 size, float3x3 transformation); + + /* Transform the domain by the given transformation. This effectively pre-multiply the given + * transformation by the current transformation of the domain. */ + void transform(const float3x3 &transformation); + + /* Returns a domain of size 1x1 and an identity transformation. */ + static Domain identity(); +}; + +/* Compare the size and transformation of the domain. The realization_options are not compared + * because they only describe the method of realization on another domain, which is not technically + * a property of the domain itself. */ +bool operator==(const Domain &a, const Domain &b); + +/* Inverse of the above equality operator. */ +bool operator!=(const Domain &a, const Domain &b); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_evaluator.hh b/source/blender/compositor/realtime_compositor/COM_evaluator.hh new file mode 100644 index 00000000000..fd6feb0948b --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_evaluator.hh @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> + +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_context.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_shader_operation.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Evaluator + * + * The evaluator is the main class of the compositor and the entry point of its execution. The + * evaluator compiles the compositor node tree and evaluates it to compute its output. It is + * constructed from a compositor node tree and a compositor context. Upon calling the evaluate + * method, the evaluator will check if the node tree is already compiled into an operations stream, + * and if it is, it will go over it and evaluate the operations in order. It is then the + * responsibility of the caller to call the reset method when the node tree changes to invalidate + * the operations stream. A reset is also required if the resources used by the node tree change, + * for instances, when the dimensions of an image used by the node tree changes. This is necessary + * because the evaluator compiles the node tree into an operations stream that is specifically + * optimized for the structure of the resources used by the node tree. + * + * Otherwise, if the node tree is not yet compiled, the evaluator will compile it into an + * operations stream, evaluating the operations in the process. It should be noted that operations + * are evaluated as soon as they are compiled, as opposed to compiling the whole operations stream + * and then evaluating it in a separate step. This is important because, as mentioned before, the + * operations stream is optimized specifically for the structure of the resources used by the node + * tree, which is only known after the operations are evaluated. In other words, the evaluator uses + * the evaluated results of previously compiled operations to compile the operations that follow + * them in an optimized manner. + * + * Compilation starts by computing an optimized node execution schedule by calling the + * compute_schedule function, see the discussion in COM_scheduler.hh for more details. For the node + * tree shown below, the execution schedule is denoted by the node numbers. The compiler then goes + * over the execution schedule in order and compiles each node into either a Node Operation or a + * Shader Operation, depending on the node type, see the is_shader_node function. A Shader + * operation is constructed from a group of nodes forming a contiguous subset of the node execution + * schedule. For instance, in the node tree shown below, nodes 3 and 4 are compiled together into a + * shader operation and node 5 is compiled into its own shader operation, both of which are + * contiguous subsets of the node execution schedule. This process is described in details in the + * following section. + * + * Shader Operation 1 Shader Operation 2 + * +-----------------------------------+ +------------------+ + * .------------. | .------------. .------------. | | .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | | | Node 5 | | | Node 6 | + * | |----|--| |--| |---|-----|--| |--|--| | + * | | .-|--| | | | | .--|--| | | | | + * '------------' | | '------------' '------------' | | | '------------' | '------------' + * | +-----------------------------------+ | +------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'----------------------------------------' + * | | + * '------------' + * + * For non shader nodes, the compilation process is straight forward, the compiler instantiates a + * node operation from the node, map its inputs to the results of the outputs they are linked to, + * and evaluates the operations. However, for shader nodes, since a group of nodes can be compiled + * together into a shader operation, the compilation process is a bit involved. The compiler uses + * an instance of the Compile State class to keep track of the compilation process. The compiler + * state stores the so called "shader compile unit", which is the current group of nodes that will + * eventually be compiled together into a shader operation. While going over the schedule, the + * compiler adds the shader nodes to the compile unit until it decides that the compile unit is + * complete and should be compiled. This is typically decided when the current node is not + * compatible with the compile unit and can't be added to it, only then it compiles the compile + * unit into a shader operation and resets it to ready it to track the next potential group of + * nodes that will form a shader operation. This decision is made based on various criteria in the + * should_compile_shader_compile_unit function. See the discussion in COM_compile_state.hh for more + * details of those criteria, but perhaps the most evident of which is whether the node is actually + * a shader node, if it isn't, then it evidently can't be added to the compile unit and the compile + * unit is should be compiled. + * + * For the node tree above, the compilation process is as follows. The compiler goes over the node + * execution schedule in order considering each node. Nodes 1 and 2 are not shader node so they are + * compiled into node operations and added to the operations stream. The current compile unit is + * empty, so it is not compiled. Node 3 is a shader node, and since the compile unit is currently + * empty, it is unconditionally added to it. Node 4 is a shader node, it was decided---for the sake + * of the demonstration---that it is compatible with the compile unit and can be added to it. Node + * 5 is a shader node, but it was decided---for the sake of the demonstration---that it is not + * compatible with the compile unit, so the compile unit is considered complete and is compiled + * first, adding the first shader operation to the operations stream and resetting the compile + * unit. Node 5 is then added to the now empty compile unit similar to node 3. Node 6 is not a + * shader node, so the compile unit is considered complete and is compiled first, adding the first + * shader operation to the operations stream and resetting the compile unit. Finally, node 6 is + * compiled into a node operation similar to nodes 1 and 2 and added to the operations stream. */ +class Evaluator { + private: + /* A reference to the compositor context. */ + Context &context_; + /* A reference to the compositor node tree. */ + bNodeTree &node_tree_; + /* The derived and reference node trees representing the compositor node tree. Those are + * initialized when the node tree is compiled and freed when the evaluator resets. */ + NodeTreeRefMap node_tree_reference_map_; + std::unique_ptr<DerivedNodeTree> derived_node_tree_; + /* The compiled operations stream. This contains ordered pointers to the operations that were + * compiled. This is initialized when the node tree is compiled and freed when the evaluator + * resets. The is_compiled_ member indicates whether the operation stream can be used or needs to + * be compiled first. Note that the operations stream can be empty even when compiled, this can + * happen when the node tree is empty or invalid for instance. */ + Vector<std::unique_ptr<Operation>> operations_stream_; + /* True if the node tree is already compiled into an operations stream that can be evaluated + * directly. False if the node tree is not compiled yet and needs to be compiled. */ + bool is_compiled_ = false; + + public: + /* Construct an evaluator from a compositor node tree and a context. */ + Evaluator(Context &context, bNodeTree &node_tree); + + /* Evaluate the compositor node tree. If the node tree is already compiled into an operations + * stream, that stream will be evaluated directly. Otherwise, the node tree will be compiled and + * evaluated. */ + void evaluate(); + + /* Invalidate the operations stream that was compiled for the node tree. This should be called + * when the node tree changes or the structure of any of the resources used by it changes. By + * structure, we mean things like the dimensions of the used images, while changes to their + * contents do not necessitate a reset. */ + void reset(); + + private: + /* Check if the compositor node tree is valid by checking if it has: + * - Cyclic links. + * - Undefined nodes or sockets. + * - Unsupported nodes. + * If the node tree is valid, true is returned. Otherwise, false is returned, and an appropriate + * error message is set by calling the context's set_info_message method. */ + bool validate_node_tree(); + + /* Compile the node tree into an operations stream and evaluate it. */ + void compile_and_evaluate(); + + /* Compile the given node into a node operation, map each input to the result of the output + * linked to it, update the compile state, add the newly created operation to the operations + * stream, and evaluate the operation. */ + void compile_and_evaluate_node(DNode node, CompileState &compile_state); + + /* Map each input of the node operation to the result of the output linked to it. Unlinked inputs + * are mapped to the result of a newly created Input Single Value Operation, which is added to + * the operations stream and evaluated. Since this method might add operations to the operations + * stream, the actual node operation should only be added to the stream once this method is + * called. */ + void map_node_operation_inputs_to_their_results(DNode node, + NodeOperation *operation, + CompileState &compile_state); + + /* Compile the shader compile unit into a shader operation, map each input of the operation to + * the result of the output linked to it, update the compile state, add the newly created + * operation to the operations stream, evaluate the operation, and finally reset the shader + * compile unit. */ + void compile_and_evaluate_shader_compile_unit(CompileState &compile_state); + + /* Map each input of the shader operation to the result of the output linked to it. */ + void map_shader_operation_inputs_to_their_results(ShaderOperation *operation, + CompileState &compile_state); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh new file mode 100644 index 00000000000..215a92ab3b4 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_input_descriptor.hh @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Input Descriptor + * + * A class that describes an input of an operation. */ +class InputDescriptor { + public: + /* The type of input. This may be different that the type of result that the operation will + * receive for the input, in which case, an implicit conversion operation will be added as an + * input processor to convert it to the required type. */ + ResultType type; + /* If true, then the input does not need to be realized on the domain of the operation before its + * execution. See the discussion in COM_domain.hh for more information. */ + bool skip_realization = false; + /* The priority of the input for determining the operation domain. The non-single value input + * with the highest priority will be used to infer the operation domain, the highest priority + * being zero. See the discussion in COM_domain.hh for more information. */ + int domain_priority = 0; + /* If true, the input expects a single value, and if a non-single value is provided, a default + * single value will be used instead, see the get_<type>_value_default methods in the Result + * class. It follows that this also implies skip_realization, because we don't need to realize a + * result that will be discarded anyways. If false, the input can work with both single and + * non-single values. */ + bool expects_single_value = false; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh new file mode 100644 index 00000000000..bbcd336ee11 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_input_single_value_operation.hh @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Input Single Value Operation + * + * An input single value operation is an operation that outputs a single value result whose value + * is the value of an unlinked input socket. This is typically used to initialize the values of + * unlinked node input sockets. */ +class InputSingleValueOperation : public Operation { + private: + /* The identifier of the output. */ + static const StringRef output_identifier_; + /* The input socket whose value will be computed as the operation's result. */ + DInputSocket input_socket_; + + public: + InputSingleValueOperation(Context &context, DInputSocket input_socket); + + /* Allocate a single value result and set its value to the default value of the input socket. */ + void execute() override; + + /* Get a reference to the output result of the operation, this essentially calls the super + * get_result with the output identifier of the operation. */ + Result &get_result(); + + private: + /* Populate the result of the operation, this essentially calls the super populate_result method + * with the output identifier of the operation. */ + void populate_result(Result result); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_node_operation.hh b/source/blender/compositor/realtime_compositor/COM_node_operation.hh new file mode 100644 index 00000000000..94bc4dfd39d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_node_operation.hh @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_scheduler.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Node Operation + * + * A node operation is a subclass of operation that nodes should implement and instantiate in the + * get_compositor_operation function of bNodeType, passing the inputs given to that function to the + * constructor. This class essentially just implements a default constructor that populates output + * results for all outputs of the node as well as input descriptors for all inputs of the nodes + * based on their socket declaration. The class also provides some utility methods for easier + * implementation of nodes. */ +class NodeOperation : public Operation { + private: + /* The node that this operation represents. */ + DNode node_; + + public: + /* Populate the output results based on the node outputs and populate the input descriptors based + * on the node inputs. */ + NodeOperation(Context &context, DNode node); + + /* Compute and set the initial reference counts of all the results of the operation. The + * reference counts of the results are the number of operations that use those results, which is + * computed as the number of inputs whose node is part of the schedule and is linked to the + * output corresponding to each result. The node execution schedule is given as an input. */ + void compute_results_reference_counts(const Schedule &schedule); + + protected: + /* Returns a reference to the derived node that this operation represents. */ + const DNode &node() const; + + /* Returns a reference to the node that this operation represents. */ + const bNode &bnode() const; + + /* Returns true if the output identified by the given identifier is needed and should be + * computed, otherwise returns false. */ + bool should_compute_output(StringRef identifier); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_operation.hh b/source/blender/compositor/realtime_compositor/COM_operation.hh new file mode 100644 index 00000000000..38518c00c04 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_operation.hh @@ -0,0 +1,175 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> +#include <string> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +class SimpleOperation; + +/* A type representing a vector of simple operations that store the input processors for a + * particular input. */ +using ProcessorsVector = Vector<std::unique_ptr<SimpleOperation>>; + +/* ------------------------------------------------------------------------------------------------ + * Operation + * + * The operation is the basic unit of the compositor. The evaluator compiles the compositor node + * tree into an ordered stream of operations which are then executed in order during evaluation. + * The operation class can be sub-classed to implement a new operation. Operations have a number of + * inputs and outputs that are declared during construction and are identified by string + * identifiers. Inputs are declared by calling declare_input_descriptor providing an appropriate + * descriptor. Those inputs are mapped to the results computed by other operations whose outputs + * are linked to the inputs. Such mappings are established by the compiler during compilation by + * calling the map_input_to_result method. Outputs are populated by calling the populate_result + * method, providing a result of an appropriate type. Upon execution, the operation allocates a + * result for each of its outputs and computes their value based on its inputs and options. + * + * Each input may have one or more input processors, which are simple operations that process the + * inputs before the operation is executed, see the discussion in COM_simple_operation.hh for more + * information. And thus the effective input of the operation is the result of the last input + * processor if one exists. Input processors are added and evaluated by calling the + * add_and_evaluate_input_processors method, which provides a default implementation that does + * things like implicit conversion, domain realization, and more. This default implementation can, + * however, be overridden, extended, or removed. Once the input processors are added and evaluated + * for the first time, they are stored in the operation and future evaluations can evaluate them + * directly without having to add them again. + * + * The operation is evaluated by calling the evaluate method, which first adds the input processors + * if they weren't added already and evaluates them, then it resets the results of the operation, + * then it calls the execute method of the operation, and finally it releases the results mapped to + * the inputs to declare that they are no longer needed. */ +class Operation { + private: + /* A reference to the compositor context. This member references the same object in all + * operations but is included in the class for convenience. */ + Context &context_; + /* A mapping between each output of the operation identified by its identifier and the result for + * that output. A result for each output of the operation should be constructed and added to the + * map during operation construction by calling the populate_result method. The results should be + * allocated and their contents should be computed in the execute method. */ + Map<std::string, Result> results_; + /* A mapping between each input of the operation identified by its identifier and its input + * descriptor. Those descriptors should be declared during operation construction by calling the + * declare_input_descriptor method. */ + Map<std::string, InputDescriptor> input_descriptors_; + /* A mapping between each input of the operation identified by its identifier and a pointer to + * the computed result providing its data. The mapped result is either one that was computed by + * another operation or one that was internally computed in the operation by the last input + * processor for that input. It is the responsibility of the evaluator to map the inputs to their + * linked results before evaluating the operation by calling the map_input_to_result method. */ + Map<StringRef, Result *> results_mapped_to_inputs_; + /* A mapping between each input of the operation identified by its identifier and an ordered list + * of simple operations to process that input. This is initialized the first time the input + * processors are evaluated by calling the add_and_evaluate_input_processors method. Further + * evaluations will evaluate the processors directly without the need to add them again. The + * input_processors_added_ member indicates whether the processors were already added and can be + * evaluated directly or need to be added and evaluated. */ + Map<StringRef, ProcessorsVector> input_processors_; + /* True if the input processors were already added and can be evaluated directly. False if the + * input processors are not yet added and needs to be added. */ + bool input_processors_added_ = false; + + public: + Operation(Context &context); + + virtual ~Operation(); + + /* Evaluate the operation by: + * 1. Evaluating the input processors. + * 2. Resetting the results of the operation. + * 3. Calling the execute method of the operation. + * 4. Releasing the results mapped to the inputs. */ + void evaluate(); + + /* Get a reference to the output result identified by the given identifier. */ + Result &get_result(StringRef identifier); + + /* Map the input identified by the given identifier to the result providing its data. See + * results_mapped_to_inputs_ for more details. This should be called by the evaluator to + * establish links between different operations. */ + void map_input_to_result(StringRef identifier, Result *result); + + protected: + /* Compute the operation domain of this operation. By default, this implements a default logic + * that infers the operation domain from the inputs, which may be overridden for a different + * logic. See the discussion in COM_domain.hh for the inference logic and more information. */ + virtual Domain compute_domain(); + + /* Add and evaluate any needed input processors, which essentially just involves calling the + * add_and_evaluate_input_processor method with the needed processors. This is called before + * executing the operation to prepare its inputs. The class defines a default implementation + * which adds typically needed processors, but derived classes can override the method to have + * a different implementation, extend the implementation, or remove it entirely. */ + virtual void add_and_evaluate_input_processors(); + + /* Given the identifier of an input of the operation and a processor operation: + * - Add the given processor to the list of input processors for the input. + * - Map the input of the processor to be the result of the last input processor or the result + * mapped to the input if no previous processors exists. + * - Switch the result mapped to the input to be the output result of the processor. + * - Evaluate the processor. */ + void add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor); + + /* This method should allocate the operation results, execute the operation, and compute the + * output results. */ + virtual void execute() = 0; + + /* Get a reference to the result connected to the input identified by the given identifier. */ + Result &get_input(StringRef identifier) const; + + /* Switch the result mapped to the input identified by the given identifier with the given + * result. */ + void switch_result_mapped_to_input(StringRef identifier, Result *result); + + /* Add the given result to the results_ map identified by the given output identifier. This + * should be called during operation construction for all outputs. The provided result shouldn't + * be allocated or initialized, this will happen later during execution. */ + void populate_result(StringRef identifier, Result result); + + /* Declare the descriptor of the input identified by the given identifier to be the given + * descriptor. Adds the given descriptor to the input_descriptors_ map identified by the given + * input identifier. This should be called during operation constructor for all inputs. */ + void declare_input_descriptor(StringRef identifier, InputDescriptor descriptor); + + /* Get a reference to the descriptor of the input identified by the given identified. */ + InputDescriptor &get_input_descriptor(StringRef identifier); + + /* Returns a reference to the compositor context. */ + Context &context(); + + /* Returns a reference to the texture pool of the compositor context. */ + TexturePool &texture_pool() const; + + /* Returns a reference to the shader manager of the compositor context. */ + StaticShaderManager &shader_manager() const; + + private: + /* Evaluate the input processors. If the input processors were already added they will be + * evaluated directly. Otherwise, the input processors will be added and evaluated. */ + void evaluate_input_processors(); + + /* Resets the results of the operation. See the reset method in the Result class for more + * information. */ + void reset_results(); + + /* Release the results that are mapped to the inputs of the operation. This is called after the + * evaluation of the operation to declare that the results are no longer needed by this + * operation. */ + void release_inputs(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh new file mode 100644 index 00000000000..5a842e16008 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_realize_on_domain_operation.hh @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Realize On Domain Operation + * + * A simple operation that projects the input on a certain target domain, copies the area of the + * input that intersects the target domain, and fill the rest with zeros or repetitions of the + * input depending on the realization options of the target domain. See the discussion in + * COM_domain.hh for more information. */ +class RealizeOnDomainOperation : public SimpleOperation { + private: + /* The target domain to realize the input on. */ + Domain domain_; + + public: + RealizeOnDomainOperation(Context &context, Domain domain, ResultType type); + + void execute() override; + + /* Determine if a realize on domain operation is needed for the input with the given result and + * descriptor in an operation with the given operation domain. If it is not needed, return a null + * pointer. If it is needed, return an instance of the operation. */ + static SimpleOperation *construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor, + const Domain &operation_domain); + + protected: + /* The operation domain is just the target domain. */ + Domain compute_domain() override; + + private: + /* Get the realization shader of the appropriate type. */ + GPUShader *get_realization_shader(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh new file mode 100644 index 00000000000..2f5a82c79b7 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_reduce_to_single_value_operation.hh @@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "COM_context.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Reduce To Single Value Operation + * + * A simple operation that reduces its input result into a single value output result. The input is + * assumed to be a texture result of size 1x1, that is, a texture composed of a single pixel, the + * value of which shall serve as the single value of the output result. */ +class ReduceToSingleValueOperation : public SimpleOperation { + public: + ReduceToSingleValueOperation(Context &context, ResultType type); + + /* Download the input pixel from the GPU texture and set its value to the value of the allocated + * single value output result. */ + void execute() override; + + /* Determine if a reduce to single value operation is needed for the input with the + * given result. If it is not needed, return a null pointer. If it is needed, return an instance + * of the operation. */ + static SimpleOperation *construct_if_needed(Context &context, const Result &input_result); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_result.hh b/source/blender/compositor/realtime_compositor/COM_result.hh new file mode 100644 index 00000000000..a16d68bb92d --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_result.hh @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_domain.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* Possible data types that operations can operate on. They either represent the base type of the + * result texture or a single value result. */ +enum class ResultType : uint8_t { + Float, + Vector, + Color, +}; + +/* ------------------------------------------------------------------------------------------------ + * Result + * + * A result represents the computed value of an output of an operation. A result can either + * represent an image or a single value. A result is typed, and can be of type color, vector, or + * float. Single value results are stored in 1x1 textures to make them easily accessible in + * shaders. But the same value is also stored in the value union member of the result for any + * host-side processing. The texture of the result is allocated from the texture pool referenced by + * the result. + * + * Results are reference counted and their textures are released once their reference count reaches + * zero. After constructing a result, the set_initial_reference_count method is called to declare + * the number of operations that needs this result. Once each operation that needs the result no + * longer needs it, the release method is called and the reference count is decremented, until it + * reaches zero, where the result's texture is then released. Since results are eventually + * decremented to zero by the end of every evaluation, the reference count is restored before every + * evaluation to its initial reference count by calling the reset method, which is why a separate + * member initial_reference_count_ is stored to keep track of the initial value. + * + * A result not only represents an image, but also the area it occupies in the virtual compositing + * space. This area is called the Domain of the result, see the discussion in COM_domain.hh for + * more information. + * + * A result can be a proxy result that merely wraps another master result, in which case, it shares + * its values and delegates all reference counting to it. While a proxy result shares the value of + * the master result, it can have a different domain. Consequently, transformation operations are + * implemented using proxy results, where their results are proxy results of their inputs but with + * their domains transformed based on their options. Moreover, proxy results can also be used as + * the results of identity operations, that is, operations that do nothing to their inputs in + * certain configurations. In which case, the proxy result is left as is with no extra + * transformation on its domain whatsoever. Proxy results can be created by calling the + * pass_through method, see that method for more details. */ +class Result { + private: + /* The base type of the texture or the type of the single value. */ + ResultType type_; + /* If true, the result is a single value, otherwise, the result is a texture. */ + bool is_single_value_; + /* A GPU texture storing the result data. This will be a 1x1 texture if the result is a single + * value, the value of which will be identical to that of the value member. See class description + * for more information. */ + GPUTexture *texture_ = nullptr; + /* The texture pool used to allocate the texture of the result, this should be initialized during + * construction. */ + TexturePool *texture_pool_ = nullptr; + /* The number of operations that currently needs this result. At the time when the result is + * computed, this member will have a value that matches initial_reference_count_. Once each + * operation that needs the result no longer needs it, the release method is called and the + * reference count is decremented, until it reaches zero, where the result's texture is then + * released. If this result have a master result, then this reference count is irrelevant and + * shadowed by the reference count of the master result. */ + int reference_count_; + /* The number of operations that reference and use this result at the time when it was initially + * computed. Since reference_count_ is decremented and always becomes zero at the end of the + * evaluation, this member is used to reset the reference count of the results for later + * evaluations by calling the reset method. This member is also used to determine if this result + * should be computed by calling the should_compute method. */ + int initial_reference_count_; + /* If the result is a single value, this member stores the value of the result, the value of + * which will be identical to that stored in the texture member. The active union member depends + * on the type of the result. This member is uninitialized and should not be used if the result + * is a texture. */ + union { + float float_value_; + float3 vector_value_; + float4 color_value_; + }; + /* The domain of the result. This only matters if the result was a texture. See the discussion in + * COM_domain.hh for more information. */ + Domain domain_ = Domain::identity(); + /* If not nullptr, then this result wraps and shares the value of another master result. In this + * case, calls to texture-related methods like increment_reference_count and release should + * operate on the master result as opposed to this result. This member is typically set upon + * calling the pass_through method, which sets this result to be the master of a target result. + * See that method for more information. */ + Result *master_ = nullptr; + + public: + /* Construct a result of the given type with the given texture pool that will be used to allocate + * and release the result's texture. */ + Result(ResultType type, TexturePool &texture_pool); + + /* Declare the result to be a texture result, allocate a texture of an appropriate type with + * the size of the given domain from the result's texture pool, and set the domain of the result + * to the given domain. */ + void allocate_texture(Domain domain); + + /* Declare the result to be a single value result, allocate a texture of an appropriate + * type with size 1x1 from the result's texture pool, and set the domain to be an identity + * domain. See class description for more information. */ + void allocate_single_value(); + + /* Allocate a single value result and set its value to zero. This is called for results whose + * value can't be computed and are considered invalid. */ + void allocate_invalid(); + + /* Bind the texture of the result to the texture image unit with the given name in the currently + * bound given shader. This also inserts a memory barrier for texture fetches to ensure any prior + * writes to the texture are reflected before reading from it. */ + void bind_as_texture(GPUShader *shader, const char *texture_name) const; + + /* Bind the texture of the result to the image unit with the given name in the currently bound + * given shader. */ + void bind_as_image(GPUShader *shader, const char *image_name) const; + + /* Unbind the texture which was previously bound using bind_as_texture. */ + void unbind_as_texture() const; + + /* Unbind the texture which was previously bound using bind_as_image. */ + void unbind_as_image() const; + + /* Pass this result through to a target result, in which case, the target result becomes a proxy + * result with this result as its master result. This is done by making the target result a copy + * of this result, essentially having identical values between the two and consequently sharing + * the underlying texture. An exception is the initial reference count, whose value is retained + * and not copied, because it is a property of the original result and is needed for correctly + * resetting the result before the next evaluation. Additionally, this result is set to be the + * master of the target result, by setting the master member of the target. Finally, the + * reference count of the result is incremented by the reference count of the target result. See + * the discussion above for more information. */ + void pass_through(Result &target); + + /* Transform the result by the given transformation. This effectively pre-multiply the given + * transformation by the current transformation of the domain of the result. */ + void transform(const float3x3 &transformation); + + /* Get a reference to the realization options of this result. See the RealizationOptions struct + * for more information. */ + RealizationOptions &get_realization_options(); + + /* If the result is a single value result of type float, return its float value. Otherwise, an + * uninitialized value is returned. */ + float get_float_value() const; + + /* If the result is a single value result of type vector, return its vector value. Otherwise, an + * uninitialized value is returned. */ + float3 get_vector_value() const; + + /* If the result is a single value result of type color, return its color value. Otherwise, an + * uninitialized value is returned. */ + float4 get_color_value() const; + + /* Same as get_float_value but returns a default value if the result is not a single value. */ + float get_float_value_default(float default_value) const; + + /* Same as get_vector_value but returns a default value if the result is not a single value. */ + float3 get_vector_value_default(const float3 &default_value) const; + + /* Same as get_color_value but returns a default value if the result is not a single value. */ + float4 get_color_value_default(const float4 &default_value) const; + + /* If the result is a single value result of type float, set its float value and upload it to the + * texture. Otherwise, an undefined behavior is invoked. */ + void set_float_value(float value); + + /* If the result is a single value result of type vector, set its vector value and upload it to + * the texture. Otherwise, an undefined behavior is invoked. */ + void set_vector_value(const float3 &value); + + /* If the result is a single value result of type color, set its color value and upload it to the + * texture. Otherwise, an undefined behavior is invoked. */ + void set_color_value(const float4 &value); + + /* Set the value of initial_reference_count_, see that member for more details. This should be + * called after constructing the result to declare the number of operations that needs it. */ + void set_initial_reference_count(int count); + + /* Reset the result to prepare it for a new evaluation. This should be called before evaluating + * the operation that computes this result. First, set the value of reference_count_ to the value + * of initial_reference_count_ since reference_count_ may have already been decremented to zero + * in a previous evaluation. Second, set master_ to nullptr because the result may have been + * turned into a proxy result in a previous evaluation. Other fields don't need to be reset + * because they are runtime and overwritten during evaluation. */ + void reset(); + + /* Increment the reference count of the result by the given count. If this result have a master + * result, the reference count of the master result is incremented instead. */ + void increment_reference_count(int count = 1); + + /* Decrement the reference count of the result and release the its texture back into the texture + * pool if the reference count reaches zero. This should be called when an operation that used + * this result no longer needs it. If this result have a master result, the master result is + * released instead. */ + void release(); + + /* Returns true if this result should be computed and false otherwise. The result should be + * computed if its reference count is not zero, that is, its result is used by at least one + * operation. */ + bool should_compute(); + + /* Returns the type of the result. */ + ResultType type() const; + + /* Returns true if the result is a texture and false of it is a single value. */ + bool is_texture() const; + + /* Returns true if the result is a single value and false of it is a texture. */ + bool is_single_value() const; + + /* Returns the allocated GPU texture of the result. */ + GPUTexture *texture() const; + + /* Returns the reference count of the result. If this result have a master result, then the + * reference count of the master result is returned instead. */ + int reference_count() const; + + /* Returns a reference to the domain of the result. See the Domain class. */ + const Domain &domain() const; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_scheduler.hh b/source/blender/compositor/realtime_compositor/COM_scheduler.hh new file mode 100644 index 00000000000..4f778b32145 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_scheduler.hh @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_vector_set.hh" + +#include "NOD_derived_node_tree.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* A type representing the ordered set of nodes defining the schedule of node execution. */ +using Schedule = VectorSet<DNode>; + +/* Computes the execution schedule of the node tree. This is essentially a post-order depth first + * traversal of the node tree from the output node to the leaf input nodes, with informed order of + * traversal of dependencies based on a heuristic estimation of the number of needed buffers. */ +Schedule compute_schedule(DerivedNodeTree &tree); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_shader_node.hh b/source/blender/compositor/realtime_compositor/COM_shader_node.hh new file mode 100644 index 00000000000..453226ec452 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_shader_node.hh @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "GPU_material.h" + +#include "NOD_derived_node_tree.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* ------------------------------------------------------------------------------------------------ + * Shader Node + * + * A shader node encapsulates a compositor node tree that is capable of being used together with + * other shader nodes to construct a Shader Operation using the GPU material compiler. A GPU node + * stack for each of the node inputs and outputs is stored and populated during construction in + * order to represent the node as a GPU node inside the GPU material graph, see GPU_material.h for + * more information. Derived classes should implement the compile method to add the node and link + * it to the GPU material given to the method. The compiler is expected to initialize the input + * links of the node before invoking the compile method. See the discussion in + * COM_shader_operation.hh for more information. */ +class ShaderNode { + private: + /* The node that this operation represents. */ + DNode node_; + /* The GPU node stacks of the inputs of the node. Those are populated during construction in the + * populate_inputs method. The links of the inputs are initialized by the GPU material compiler + * prior to calling the compile method. There is an extra stack at the end to mark the end of the + * array, as this is what the GPU module functions expect. */ + Vector<GPUNodeStack> inputs_; + /* The GPU node stacks of the outputs of the node. Those are populated during construction in the + * populate_outputs method. There is an extra stack at the end to mark the end of the array, as + * this is what the GPU module functions expect. */ + Vector<GPUNodeStack> outputs_; + + public: + /* Construct the node by populating both its inputs and outputs. */ + ShaderNode(DNode node); + + virtual ~ShaderNode() = default; + + /* Compile the node by adding the appropriate GPU material graph nodes and linking the + * appropriate resources. */ + virtual void compile(GPUMaterial *material) = 0; + + /* Returns a contiguous array containing the GPU node stacks of each input. */ + GPUNodeStack *get_inputs_array(); + + /* Returns a contiguous array containing the GPU node stacks of each output. */ + GPUNodeStack *get_outputs_array(); + + /* Returns the GPU node stack of the input with the given identifier. */ + GPUNodeStack &get_input(StringRef identifier); + + /* Returns the GPU node stack of the output with the given identifier. */ + GPUNodeStack &get_output(StringRef identifier); + + /* Returns the GPU node link of the input with the given identifier, if the input is not linked, + * a uniform link carrying the value of the input will be created a returned. It is expected that + * the caller will use the returned link in a GPU material, otherwise, the link may not be + * properly freed. */ + GPUNodeLink *get_input_link(StringRef identifier); + + protected: + /* Returns a reference to the derived node that this operation represents. */ + const DNode &node() const; + + /* Returns a reference to the node this operations represents. */ + bNode &bnode() const; + + private: + /* Populate the inputs of the node. The input link is set to nullptr and is expected to be + * initialized by the GPU material compiler before calling the compile method. */ + void populate_inputs(); + /* Populate the outputs of the node. The output link is set to nullptr and is expected to be + * initialized by the compile method. */ + void populate_outputs(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_shader_operation.hh b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh new file mode 100644 index 00000000000..a33dcbf25be --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_shader_operation.hh @@ -0,0 +1,242 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector_set.hh" + +#include "GPU_material.h" +#include "GPU_shader.h" + +#include "gpu_shader_create_info.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_scheduler.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* A type representing a contiguous subset of the node execution schedule that will be compiled + * into a Shader Operation. */ +using ShaderCompileUnit = VectorSet<DNode>; + +/* ------------------------------------------------------------------------------------------------ + * Shader Operation + * + * An operation that evaluates a shader compiled from a contiguous subset of the node execution + * schedule using the GPU material compiler, see GPU_material.h for more information. The subset + * of the node execution schedule is called a shader compile unit, see the discussion in + * COM_compile_state.hh for more information. + * + * Consider the following node graph with a node execution schedule denoted by the number on each + * node. The compiler may decide to compile a subset of the execution schedule into a shader + * operation, in this case, the nodes from 3 to 5 were compiled together into a shader operation. + * This subset is called the shader compile unit. See the discussion in COM_evaluator.hh for more + * information on the compilation process. Each of the nodes inside the compile unit implements a + * Shader Node which is instantiated, stored in shader_nodes_, and used during compilation. See the + * discussion in COM_shader_node.hh for more information. Links that are internal to the shader + * operation are established between the input and outputs of the shader nodes, for instance, the + * links between nodes 3 and 4 as well as those between nodes 4 and 5. However, links that cross + * the boundary of the shader operation needs special handling. + * + * Shader Operation + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * Links from nodes that are not part of the shader operation to nodes that are part of the shader + * operation are considered inputs of the operation itself and are declared as such. For instance, + * the link from node 1 to node 3 is declared as an input to the operation, and the same applies + * for the links from node 2 to nodes 3 and 5. Note, however, that only one input is declared for + * each distinct output socket, so both links from node 2 share the same input of the operation. + * An input to the operation is declared for a distinct output socket as follows: + * + * - A texture is added to the shader, which will be bound to the result of the output socket + * during evaluation. + * - A GPU attribute is added to the GPU material for that output socket and is linked to the GPU + * input stack of the inputs linked to the output socket. + * - Code is emitted to initialize the values of the attributes by sampling the textures + * corresponding to each of the inputs. + * - The newly added attribute is mapped to the output socket in output_to_material_attribute_map_ + * to share that same attributes for all inputs linked to the same output socket. + * + * Links from nodes that are part of the shader operation to nodes that are not part of the shader + * operation are considered outputs of the operation itself and are declared as such. For instance, + * the link from node 5 to node 6 is declared as an output to the operation. An output to the + * operation is declared for an output socket as follows: + * + * - An image is added in the shader where the output value will be written. + * - A storer GPU material node that stores the value of the output is added and linked to the GPU + * output stack of the output. The storer will store the value in the image identified by the + * index of the output given to the storer. + * - The storer functions are generated dynamically to map each index with its appropriate image. + * + * The GPU material code generator source is used to construct a compute shader that is then + * dispatched during operation evaluation after binding the inputs, outputs, and any necessary + * resources. */ +class ShaderOperation : public Operation { + private: + /* The compile unit that will be compiled into this shader operation. */ + ShaderCompileUnit compile_unit_; + /* The GPU material backing the operation. This is created and compiled during construction and + * freed during destruction. */ + GPUMaterial *material_; + /* A map that associates each node in the compile unit with an instance of its shader node. */ + Map<DNode, std::unique_ptr<ShaderNode>> shader_nodes_; + /* A map that associates the identifier of each input of the operation with the output socket it + * is linked to. This is needed to help the compiler establish links between operations. */ + Map<std::string, DOutputSocket> inputs_to_linked_outputs_map_; + /* A map that associates the output socket that provides the result of an output of the operation + * with the identifier of that output. This is needed to help the compiler establish links + * between operations. */ + Map<DOutputSocket, std::string> output_sockets_to_output_identifiers_map_; + /* A map that associates the output socket of a node that is not part of the shader operation to + * the attribute that was created for it. This is used to share the same attribute with all + * inputs that are linked to the same output socket. */ + Map<DOutputSocket, GPUNodeLink *> output_to_material_attribute_map_; + + public: + /* Construct and compile a GPU material from the given shader compile unit by calling + * GPU_material_from_callbacks with the appropriate callbacks. */ + ShaderOperation(Context &context, ShaderCompileUnit &compile_unit); + + /* Free the GPU material. */ + ~ShaderOperation(); + + /* Allocate the output results, bind the shader and all its needed resources, then dispatch the + * shader. */ + void execute() override; + + /* Get the identifier of the operation output corresponding to the given output socket. This is + * called by the compiler to identify the operation output that provides the result for an input + * by providing the output socket that the input is linked to. See + * output_sockets_to_output_identifiers_map_ for more information. */ + StringRef get_output_identifier_from_output_socket(DOutputSocket output_socket); + + /* Get a reference to the inputs to linked outputs map of the operation. This is called by the + * compiler to identify the output that each input of the operation is linked to for correct + * input mapping. See inputs_to_linked_outputs_map_ for more information. */ + Map<std::string, DOutputSocket> &get_inputs_to_linked_outputs_map(); + + /* Compute and set the initial reference counts of all the results of the operation. The + * reference counts of the results are the number of operations that use those results, which is + * computed as the number of inputs whose node is part of the schedule and is linked to the + * output corresponding to each of the results of the operation. The node execution schedule is + * given as an input. */ + void compute_results_reference_counts(const Schedule &schedule); + + private: + /* Bind the uniform buffer of the GPU material as well as any color band textures needed by the + * GPU material. The compiled shader of the material is given as an argument and assumed to be + * bound. */ + void bind_material_resources(GPUShader *shader); + + /* Bind the input results of the operation to the appropriate textures in the GPU material. The + * attributes stored in output_to_material_attribute_map_ have names that match the texture + * samplers in the shader as well as the identifiers of the operation inputs that they correspond + * to. The compiled shader of the material is given as an argument and assumed to be bound. */ + void bind_inputs(GPUShader *shader); + + /* Bind the output results of the operation to the appropriate images in the GPU material. The + * name of the images in the shader match the identifier of their corresponding outputs. The + * compiled shader of the material is given as an argument and assumed to be bound. */ + void bind_outputs(GPUShader *shader); + + /* A static callback method of interface ConstructGPUMaterialFn that is passed to + * GPU_material_from_callbacks to construct the GPU material graph. The thunk parameter will be a + * pointer to the instance of ShaderOperation that is being compiled. The method goes over the + * compile unit and does the following for each node: + * + * - Instantiate a ShaderNode from the node and add it to shader_nodes_. + * - Link the inputs of the node if needed. The inputs are either linked to other nodes in the + * GPU material graph or are exposed as inputs to the shader operation itself if they are + * linked to nodes that are not part of the shader operation. + * - Call the compile method of the shader node to actually add and link the GPU material graph + * nodes. + * - If any of the outputs of the node are linked to nodes that are not part of the shader + * operation, they are exposed as outputs to the shader operation itself. */ + static void construct_material(void *thunk, GPUMaterial *material); + + /* Link the inputs of the node if needed. Unlinked inputs are ignored as they will be linked by + * the node compile method. If the input is linked to a node that is not part of the shader + * operation, the input will be exposed as an input to the shader operation and linked to it. + * While if the input is linked to a node that is part of the shader operation, then it is linked + * to that node in the GPU material node graph. */ + void link_node_inputs(DNode node, GPUMaterial *material); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is also part of the shader operation, just link the output + * link of the GPU node stack of the output socket to the input link of the GPU node stack of the + * input socket. This essentially establishes the needed links in the GPU material node graph. */ + void link_node_input_internal(DInputSocket input_socket, DOutputSocket output_socket); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is not part of the shader operation, declare a new + * operation input and link it to the input link of the GPU node stack of the input socket. An + * operation input is only declared if no input was already declared for that same output socket + * before. */ + void link_node_input_external(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material); + + /* Given the input socket of a node that is part of the shader operation which is linked to the + * given output socket of a node that is not part of the shader operation, declare a new input to + * the operation that is represented in the GPU material by a newly created GPU attribute. It is + * assumed that no operation input was declared for this same output socket before. In the + * generate_code_for_inputs method, a texture will be added in the shader for each of the + * declared inputs, having the same name as the attribute. Additionally, code will be emitted to + * initialize the attributes by sampling their corresponding textures. */ + void declare_operation_input(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material); + + /* Populate the output results of the shader operation for output sockets of the given node that + * are linked to nodes outside of the shader operation. */ + void populate_results_for_node(DNode node, GPUMaterial *material); + + /* Given the output socket of a node that is part of the shader operation which is linked to an + * input socket of a node that is not part of the shader operation, declare a new output to the + * operation and link it to an output storer passing in the index of the output. In the + * generate_code_for_outputs method, an image will be added in the shader for each of the + * declared outputs. Additionally, code will be emitted to define the storer functions that store + * the value in the appropriate image identified by the given index. */ + void populate_operation_result(DOutputSocket output_socket, GPUMaterial *material); + + /* A static callback method of interface GPUCodegenCallbackFn that is passed to + * GPU_material_from_callbacks to create the shader create info of the GPU material. The thunk + * parameter will be a pointer to the instance of ShaderOperation that is being compiled. + * + * This method first generates the necessary code to load the inputs and store the outputs. Then, + * it creates a compute shader from the generated sources. Finally, it adds the necessary GPU + * resources to the shader. */ + static void generate_code(void *thunk, GPUMaterial *material, GPUCodegenOutput *code_generator); + + /* Add an image in the shader for each of the declared outputs. Additionally, emit code to define + * the storer functions that store the given value in the appropriate image identified by the + * given index. */ + void generate_code_for_outputs(gpu::shader::ShaderCreateInfo &shader_create_info); + + /* Add a texture will in the shader for each of the declared inputs/attributes in the operation, + * having the same name as the attribute. Additionally, emit code to initialize the attributes by + * sampling their corresponding textures. */ + void generate_code_for_inputs(GPUMaterial *material, + gpu::shader::ShaderCreateInfo &shader_create_info); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_simple_operation.hh b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh new file mode 100644 index 00000000000..1655e52ac9a --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_simple_operation.hh @@ -0,0 +1,64 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_string_ref.hh" + +#include "COM_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Simple Operation + * + * A simple operation is an operation that takes exactly one input and computes exactly one output. + * Moreover, the output is guaranteed to only have a single user, that is, its reference count will + * be one. Such operations can be attached to the inputs of operations to pre-process the inputs to + * prepare them before the operation is executed.*/ +class SimpleOperation : public Operation { + private: + /* The identifier of the output. This is constant for all operations. */ + static const StringRef output_identifier_; + /* The identifier of the input. This is constant for all operations. */ + static const StringRef input_identifier_; + + public: + using Operation::Operation; + + /* Get a reference to the output result of the operation, this essentially calls the super + * get_result method with the output identifier of the operation. */ + Result &get_result(); + + /* Map the input of the operation to the given result, this essentially calls the super + * map_input_to_result method with the input identifier of the operation. */ + void map_input_to_result(Result *result); + + protected: + /* Simple operations don't need input processors, so override with an empty implementation. */ + void add_and_evaluate_input_processors() override; + + /* Get a reference to the input result of the operation, this essentially calls the super + * get_result method with the input identifier of the operation. */ + Result &get_input(); + + /* Switch the result mapped to the input with the given result, this essentially calls the super + * switch_result_mapped_to_input method with the input identifier of the operation. */ + void switch_result_mapped_to_input(Result *result); + + /* Populate the result of the operation, this essentially calls the super populate_result method + * with the output identifier of the operation and sets the initial reference count of the result + * to 1, since the result of an operation is guaranteed to have a single user. */ + void populate_result(Result result); + + /* Declare the descriptor of the input of the operation to be the given descriptor, this + * essentially calls the super declare_input_descriptor method with the input identifier of the + * operation. */ + void declare_input_descriptor(InputDescriptor descriptor); + + /* Get a reference to the descriptor of the input, this essentially calls the super + * get_input_descriptor method with the input identifier of the operation. */ + InputDescriptor &get_input_descriptor(); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh new file mode 100644 index 00000000000..161a80862a0 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_static_shader_manager.hh @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" + +#include "GPU_shader.h" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Static Shader Manager + * + * A static shader manager is a map of shaders identified by their info name that can be acquired + * and reused throughout the evaluation of the compositor and are only freed when the shader + * manager is destroyed. Once a shader is acquired for the first time, it will be cached in the + * manager to be potentially acquired later if needed without the shader creation overhead. */ +class StaticShaderManager { + private: + /* The set of shaders identified by their info name that are currently available in the manager + * to be acquired. */ + Map<StringRef, GPUShader *> shaders_; + + public: + ~StaticShaderManager(); + + /* Check if there is an available shader with the given info name in the manager, if such shader + * exists, return it, otherwise, return a newly created shader and add it to the manager. */ + GPUShader *get(const char *info_name); +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_texture_pool.hh b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh new file mode 100644 index 00000000000..cc6641d288f --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_texture_pool.hh @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include <cstdint> + +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_vector.hh" + +#include "GPU_texture.h" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------ + * Texture Pool Key + * + * A key used to identify a texture specification in a texture pool. Defines a hash and an equality + * operator for use in a hash map. */ +class TexturePoolKey { + public: + int2 size; + eGPUTextureFormat format; + + /* Construct a key from the given texture size and format. */ + TexturePoolKey(int2 size, eGPUTextureFormat format); + + /* Construct a key from the size and format of the given texture. */ + TexturePoolKey(const GPUTexture *texture); + + uint64_t hash() const; +}; + +bool operator==(const TexturePoolKey &a, const TexturePoolKey &b); + +/* ------------------------------------------------------------------------------------------------ + * Texture Pool + * + * A texture pool allows the allocation and reuse of textures throughout the execution of the + * compositor to avoid memory fragmentation and texture allocation overheads. The texture pool + * delegates the actual texture allocation to an allocate_texture method that should be implemented + * by the caller of the compositor evaluator, allowing a more agnostic and flexible execution that + * can be controlled by the caller. If the compositor is expected to execute frequently, like on + * every redraw, then the allocation method should use a persistent texture pool to allow + * cross-evaluation texture pooling, for instance, by using the DRWTexturePool. But if the + * evaluator is expected to execute infrequently, the allocated textures can just be freed when the + * evaluator is done, that is, when the pool is destructed. */ +class TexturePool { + private: + /* The set of textures in the pool that are available to acquire for each distinct texture + * specification. */ + Map<TexturePoolKey, Vector<GPUTexture *>> textures_; + + public: + /* Check if there is an available texture with the given specification in the pool, if such + * texture exists, return it, otherwise, return a newly allocated texture. Expect the texture to + * be uncleared and possibly contains garbage data. */ + GPUTexture *acquire(int2 size, eGPUTextureFormat format); + + /* Shorthand for acquire with GPU_RGBA16F format. */ + GPUTexture *acquire_color(int2 size); + + /* Shorthand for acquire with GPU_RGBA16F format. Identical to acquire_color because vectors + * are stored in RGBA textures, due to the limited support for RGB textures. */ + GPUTexture *acquire_vector(int2 size); + + /* Shorthand for acquire with GPU_R16F format. */ + GPUTexture *acquire_float(int2 size); + + /* Put the texture back into the pool, potentially to be acquired later by another user. Expects + * the texture to be one that was acquired using the same texture pool. */ + void release(GPUTexture *texture); + + /* Reset the texture pool by clearing all available textures without freeing the textures. If the + * textures will no longer be needed, they should be freed in the destructor. This should be + * called after the compositor is done evaluating. */ + void reset(); + + private: + /* Returns a newly allocated texture with the given specification. This method should be + * implemented by the caller of the compositor evaluator. See the class description for more + * information. */ + virtual GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) = 0; +}; + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/COM_utilities.hh b/source/blender/compositor/realtime_compositor/COM_utilities.hh new file mode 100644 index 00000000000..4bd61aab5cb --- /dev/null +++ b/source/blender/compositor/realtime_compositor/COM_utilities.hh @@ -0,0 +1,61 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_function_ref.hh" +#include "BLI_math_vec_types.hh" + +#include "NOD_derived_node_tree.hh" + +#include "GPU_shader.h" + +#include "COM_input_descriptor.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* Get the origin socket of the given node input. If the input is not linked, the socket itself is + * returned. If the input is linked, the socket that is linked to it is returned, which could + * either be an input or an output. An input socket is returned when the given input is connected + * to an unlinked input of a group input node. */ +DSocket get_input_origin_socket(DInputSocket input); + +/* Get the output socket linked to the given node input. If the input is not linked to an output, a + * null output is returned. */ +DOutputSocket get_output_linked_to_input(DInputSocket input); + +/* Get the result type that corresponds to the type of the given socket. */ +ResultType get_node_socket_result_type(const SocketRef *socket); + +/* Returns true if any of the nodes linked to the given output satisfies the given condition, and + * false otherwise. */ +bool is_output_linked_to_node_conditioned(DOutputSocket output, + FunctionRef<bool(DNode)> condition); + +/* Returns the number of inputs linked to the given output that satisfy the given condition. */ +int number_of_inputs_linked_to_output_conditioned(DOutputSocket output, + FunctionRef<bool(DInputSocket)> condition); + +/* A node is a shader node if it defines a method to get a shader node operation. */ +bool is_shader_node(DNode node); + +/* Returns true if the given node is supported, that is, have an implementation. Returns false + * otherwise. */ +bool is_node_supported(DNode node); + +/* Get the input descriptor of the given input socket. */ +InputDescriptor input_descriptor_from_input_socket(const InputSocketRef *socket); + +/* Dispatch the given compute shader in a 2D compute space such that the number of threads in both + * dimensions is as small as possible but at least covers the entirety of threads_range assuming + * the shader has a local group size given by local_size. That means that the number of threads + * might be a bit larger than threads_range, so shaders has to put that into consideration. A + * default local size of 16x16 is assumed, which is the optimal local size for many image + * processing shaders. */ +void compute_dispatch_threads_at_least(GPUShader *shader, + int2 threads_range, + int2 local_size = int2(16)); + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/compile_state.cc b/source/blender/compositor/realtime_compositor/intern/compile_state.cc new file mode 100644 index 00000000000..5b485224111 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/compile_state.cc @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <limits> + +#include "BLI_math_vec_types.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_node_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +CompileState::CompileState(const Schedule &schedule) : schedule_(schedule) +{ +} + +const Schedule &CompileState::get_schedule() +{ + return schedule_; +} + +void CompileState::map_node_to_node_operation(DNode node, NodeOperation *operations) +{ + return node_operations_.add_new(node, operations); +} + +void CompileState::map_node_to_shader_operation(DNode node, ShaderOperation *operations) +{ + return shader_operations_.add_new(node, operations); +} + +Result &CompileState::get_result_from_output_socket(DOutputSocket output) +{ + /* The output belongs to a node that was compiled into a standard node operation, so return a + * reference to the result from that operation using the output identifier. */ + if (node_operations_.contains(output.node())) { + NodeOperation *operation = node_operations_.lookup(output.node()); + return operation->get_result(output->identifier()); + } + + /* Otherwise, the output belongs to a node that was compiled into a shader operation, so + * retrieve the internal identifier of that output and return a reference to the result from + * that operation using the retrieved identifier. */ + ShaderOperation *operation = shader_operations_.lookup(output.node()); + return operation->get_result(operation->get_output_identifier_from_output_socket(output)); +} + +void CompileState::add_node_to_shader_compile_unit(DNode node) +{ + shader_compile_unit_.add_new(node); + + /* If the domain of the shader compile unit is not yet determined or was determined to be + * an identity domain, update it to be the computed domain of the node. */ + if (shader_compile_unit_domain_ == Domain::identity()) { + shader_compile_unit_domain_ = compute_shader_node_domain(node); + } +} + +ShaderCompileUnit &CompileState::get_shader_compile_unit() +{ + return shader_compile_unit_; +} + +void CompileState::reset_shader_compile_unit() +{ + return shader_compile_unit_.clear(); +} + +bool CompileState::should_compile_shader_compile_unit(DNode node) +{ + /* If the shader compile unit is empty, then it can't be compiled yet. */ + if (shader_compile_unit_.is_empty()) { + return false; + } + + /* If the node is not a shader node, then it can't be added to the shader compile unit and the + * shader compile unit is considered complete and should be compiled. */ + if (!is_shader_node(node)) { + return true; + } + + /* If the computed domain of the node doesn't matches the domain of the shader compile unit, then + * it can't be added to the shader compile unit and the shader compile unit is considered + * complete and should be compiled. Identity domains are an exception as they are always + * compatible because they represents single values. */ + if (shader_compile_unit_domain_ != Domain::identity() && + shader_compile_unit_domain_ != compute_shader_node_domain(node)) { + return true; + } + + /* Otherwise, the node is compatible and can be added to the compile unit and it shouldn't be + * compiled just yet. */ + return false; +} + +Domain CompileState::compute_shader_node_domain(DNode node) +{ + /* Default to an identity domain in case no domain input was found, most likely because all + * inputs are single values. */ + Domain node_domain = Domain::identity(); + int current_domain_priority = std::numeric_limits<int>::max(); + + /* Go over the inputs and find the domain of the non single value input with the highest domain + * priority. */ + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked, so skip + * it. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_ref); + + /* If the output belongs to a node that is part of the shader compile unit, then the domain of + * the input is the domain of the compile unit itself. */ + if (shader_compile_unit_.contains(output.node())) { + /* Single value inputs can't be domain inputs. */ + if (shader_compile_unit_domain_.size == int2(1)) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (input_descriptor.domain_priority < current_domain_priority) { + node_domain = shader_compile_unit_domain_; + current_domain_priority = input_descriptor.domain_priority; + } + continue; + } + + const Result &result = get_result_from_output_socket(output); + + /* A single value input can't be a domain input. */ + if (result.is_single_value() || input_descriptor.expects_single_value) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (input_descriptor.domain_priority < current_domain_priority) { + node_domain = result.domain(); + current_domain_priority = input_descriptor.domain_priority; + } + } + + return node_domain; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/context.cc b/source/blender/compositor/realtime_compositor/intern/context.cc new file mode 100644 index 00000000000..64ac29af3d1 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/context.cc @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "COM_context.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Context::Context(TexturePool &texture_pool) : texture_pool_(texture_pool) +{ +} + +int Context::get_frame_number() const +{ + return get_scene()->r.cfra; +} + +float Context::get_time() const +{ + const float frame_number = static_cast<float>(get_frame_number()); + const float frame_rate = static_cast<float>(get_scene()->r.frs_sec) / + static_cast<float>(get_scene()->r.frs_sec_base); + return frame_number / frame_rate; +} + +TexturePool &Context::texture_pool() +{ + return texture_pool_; +} + +StaticShaderManager &Context::shader_manager() +{ + return shader_manager_; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc new file mode 100644 index 00000000000..d6bf74ffbee --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/conversion_operation.cc @@ -0,0 +1,225 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" + +#include "COM_context.hh" +#include "COM_conversion_operation.hh" +#include "COM_input_descriptor.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +/* ------------------------------------------------------------------------------------------------- + * Conversion Operation. + */ + +void ConversionOperation::execute() +{ + Result &result = get_result(); + const Result &input = get_input(); + + if (input.is_single_value()) { + result.allocate_single_value(); + execute_single(input, result); + return; + } + + result.allocate_texture(input.domain()); + + GPUShader *shader = get_conversion_shader(); + GPU_shader_bind(shader); + + input.bind_as_texture(shader, "input_tx"); + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, input.domain().size); + + input.unbind_as_texture(); + result.unbind_as_image(); + GPU_shader_unbind(); +} + +SimpleOperation *ConversionOperation::construct_if_needed(Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor) +{ + ResultType result_type = input_result.type(); + ResultType expected_type = input_descriptor.type; + + /* If the result type differs from the expected type, return an instance of an appropriate + * conversion operation. Otherwise, return a null pointer. */ + + if (result_type == ResultType::Float && expected_type == ResultType::Vector) { + return new ConvertFloatToVectorOperation(context); + } + + if (result_type == ResultType::Float && expected_type == ResultType::Color) { + return new ConvertFloatToColorOperation(context); + } + + if (result_type == ResultType::Color && expected_type == ResultType::Float) { + return new ConvertColorToFloatOperation(context); + } + + if (result_type == ResultType::Color && expected_type == ResultType::Vector) { + return new ConvertColorToVectorOperation(context); + } + + if (result_type == ResultType::Vector && expected_type == ResultType::Float) { + return new ConvertVectorToFloatOperation(context); + } + + if (result_type == ResultType::Vector && expected_type == ResultType::Color) { + return new ConvertVectorToColorOperation(context); + } + + return nullptr; +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Float To Vector Operation. + */ + +ConvertFloatToVectorOperation::ConvertFloatToVectorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Float; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Vector, texture_pool())); +} + +void ConvertFloatToVectorOperation::execute_single(const Result &input, Result &output) +{ + output.set_vector_value(float3(input.get_float_value())); +} + +GPUShader *ConvertFloatToVectorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_float_to_vector"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Float To Color Operation. + */ + +ConvertFloatToColorOperation::ConvertFloatToColorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Float; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Color, texture_pool())); +} + +void ConvertFloatToColorOperation::execute_single(const Result &input, Result &output) +{ + float4 color = float4(input.get_float_value()); + color[3] = 1.0f; + output.set_color_value(color); +} + +GPUShader *ConvertFloatToColorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_float_to_color"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Color To Float Operation. + */ + +ConvertColorToFloatOperation::ConvertColorToFloatOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Color; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Float, texture_pool())); +} + +void ConvertColorToFloatOperation::execute_single(const Result &input, Result &output) +{ + float4 color = input.get_color_value(); + output.set_float_value((color[0] + color[1] + color[2]) / 3.0f); +} + +GPUShader *ConvertColorToFloatOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_color_to_float"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Color To Vector Operation. + */ + +ConvertColorToVectorOperation::ConvertColorToVectorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Color; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Vector, texture_pool())); +} + +void ConvertColorToVectorOperation::execute_single(const Result &input, Result &output) +{ + float4 color = input.get_color_value(); + output.set_vector_value(float3(color)); +} + +GPUShader *ConvertColorToVectorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_color_to_vector"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Vector To Float Operation. + */ + +ConvertVectorToFloatOperation::ConvertVectorToFloatOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Vector; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Float, texture_pool())); +} + +void ConvertVectorToFloatOperation::execute_single(const Result &input, Result &output) +{ + float3 vector = input.get_vector_value(); + output.set_float_value((vector[0] + vector[1] + vector[2]) / 3.0f); +} + +GPUShader *ConvertVectorToFloatOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_vector_to_float"); +} + +/* ------------------------------------------------------------------------------------------------- + * Convert Vector To Color Operation. + */ + +ConvertVectorToColorOperation::ConvertVectorToColorOperation(Context &context) + : ConversionOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = ResultType::Vector; + declare_input_descriptor(input_descriptor); + populate_result(Result(ResultType::Color, texture_pool())); +} + +void ConvertVectorToColorOperation::execute_single(const Result &input, Result &output) +{ + output.set_color_value(float4(input.get_vector_value(), 1.0f)); +} + +GPUShader *ConvertVectorToColorOperation::get_conversion_shader() const +{ + return shader_manager().get("compositor_convert_vector_to_color"); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/domain.cc b/source/blender/compositor/realtime_compositor/intern/domain.cc new file mode 100644 index 00000000000..31b297c212e --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/domain.cc @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "COM_domain.hh" + +namespace blender::realtime_compositor { + +Domain::Domain(int2 size) : size(size), transformation(float3x3::identity()) +{ +} + +Domain::Domain(int2 size, float3x3 transformation) : size(size), transformation(transformation) +{ +} + +void Domain::transform(const float3x3 &input_transformation) +{ + transformation = input_transformation * transformation; +} + +Domain Domain::identity() +{ + return Domain(int2(1), float3x3::identity()); +} + +bool operator==(const Domain &a, const Domain &b) +{ + return a.size == b.size && a.transformation == b.transformation; +} + +bool operator!=(const Domain &a, const Domain &b) +{ + return !(a == b); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/evaluator.cc b/source/blender/compositor/realtime_compositor/intern/evaluator.cc new file mode 100644 index 00000000000..d358389f2e9 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/evaluator.cc @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <string> + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "COM_compile_state.hh" +#include "COM_context.hh" +#include "COM_evaluator.hh" +#include "COM_input_single_value_operation.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +Evaluator::Evaluator(Context &context, bNodeTree &node_tree) + : context_(context), node_tree_(node_tree) +{ +} + +void Evaluator::evaluate() +{ + context_.texture_pool().reset(); + + if (!is_compiled_) { + compile_and_evaluate(); + is_compiled_ = true; + return; + } + + for (const std::unique_ptr<Operation> &operation : operations_stream_) { + operation->evaluate(); + } +} + +void Evaluator::reset() +{ + operations_stream_.clear(); + derived_node_tree_.reset(); + node_tree_reference_map_.clear(); + + is_compiled_ = false; +} + +bool Evaluator::validate_node_tree() +{ + if (derived_node_tree_->has_link_cycles()) { + context_.set_info_message("Compositor node tree has cyclic links!"); + return false; + } + + if (derived_node_tree_->has_undefined_nodes_or_sockets()) { + context_.set_info_message("Compositor node tree has undefined nodes or sockets!"); + return false; + } + + return true; +} + +void Evaluator::compile_and_evaluate() +{ + derived_node_tree_ = std::make_unique<DerivedNodeTree>(node_tree_, node_tree_reference_map_); + + if (!validate_node_tree()) { + return; + } + + const Schedule schedule = compute_schedule(*derived_node_tree_); + + CompileState compile_state(schedule); + + for (const DNode &node : schedule) { + if (compile_state.should_compile_shader_compile_unit(node)) { + compile_and_evaluate_shader_compile_unit(compile_state); + } + + if (is_shader_node(node)) { + compile_state.add_node_to_shader_compile_unit(node); + } + else { + compile_and_evaluate_node(node, compile_state); + } + } +} + +void Evaluator::compile_and_evaluate_node(DNode node, CompileState &compile_state) +{ + NodeOperation *operation = node->typeinfo()->get_compositor_operation(context_, node); + + compile_state.map_node_to_node_operation(node, operation); + + map_node_operation_inputs_to_their_results(node, operation, compile_state); + + /* This has to be done after input mapping because the method may add Input Single Value + * Operations to the operations stream, which needs to be evaluated before the operation itself + * is evaluated. */ + operations_stream_.append(std::unique_ptr<Operation>(operation)); + + operation->compute_results_reference_counts(compile_state.get_schedule()); + + operation->evaluate(); +} + +void Evaluator::map_node_operation_inputs_to_their_results(DNode node, + NodeOperation *operation, + CompileState &compile_state) +{ + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + DSocket origin = get_input_origin_socket(input); + + /* The origin socket is an output, which means the input is linked. So map the input to the + * result we get from the output. */ + if (origin->is_output()) { + Result &result = compile_state.get_result_from_output_socket(DOutputSocket(origin)); + operation->map_input_to_result(input->identifier(), &result); + continue; + } + + /* Otherwise, the origin socket is an input, which either means the input is unlinked and the + * origin is the input socket itself or the input is connected to an unlinked input of a group + * input node and the origin is the input of the group input node. So map the input to the + * result of a newly created Input Single Value Operation. */ + auto *input_operation = new InputSingleValueOperation(context_, DInputSocket(origin)); + operation->map_input_to_result(input->identifier(), &input_operation->get_result()); + + operations_stream_.append(std::unique_ptr<InputSingleValueOperation>(input_operation)); + + input_operation->evaluate(); + } +} + +void Evaluator::compile_and_evaluate_shader_compile_unit(CompileState &compile_state) +{ + ShaderCompileUnit &compile_unit = compile_state.get_shader_compile_unit(); + ShaderOperation *operation = new ShaderOperation(context_, compile_unit); + + for (DNode node : compile_unit) { + compile_state.map_node_to_shader_operation(node, operation); + } + + map_shader_operation_inputs_to_their_results(operation, compile_state); + + operations_stream_.append(std::unique_ptr<Operation>(operation)); + + operation->compute_results_reference_counts(compile_state.get_schedule()); + + operation->evaluate(); + + compile_state.reset_shader_compile_unit(); +} + +void Evaluator::map_shader_operation_inputs_to_their_results(ShaderOperation *operation, + CompileState &compile_state) +{ + for (const auto &item : operation->get_inputs_to_linked_outputs_map().items()) { + Result &result = compile_state.get_result_from_output_socket(item.value); + operation->map_input_to_result(item.key, &result); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc new file mode 100644 index 00000000000..0bdd40e3636 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/input_single_value_operation.cc @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_math_vec_types.hh" + +#include "COM_input_single_value_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +const StringRef InputSingleValueOperation::output_identifier_ = StringRef("Output"); + +InputSingleValueOperation::InputSingleValueOperation(Context &context, DInputSocket input_socket) + : Operation(context), input_socket_(input_socket) +{ + const ResultType result_type = get_node_socket_result_type(input_socket_.socket_ref()); + Result result = Result(result_type, texture_pool()); + + /* The result of an input single value operation is guaranteed to have a single user. */ + result.set_initial_reference_count(1); + + populate_result(result); +} + +void InputSingleValueOperation::execute() +{ + /* Allocate a single value for the result. */ + Result &result = get_result(); + result.allocate_single_value(); + + /* Set the value of the result to the default value of the input socket. */ + switch (result.type()) { + case ResultType::Float: + result.set_float_value(input_socket_->default_value<bNodeSocketValueFloat>()->value); + break; + case ResultType::Vector: + result.set_vector_value( + float3(input_socket_->default_value<bNodeSocketValueVector>()->value)); + break; + case ResultType::Color: + result.set_color_value(float4(input_socket_->default_value<bNodeSocketValueRGBA>()->value)); + break; + } +} + +Result &InputSingleValueOperation::get_result() +{ + return Operation::get_result(output_identifier_); +} + +void InputSingleValueOperation::populate_result(Result result) +{ + Operation::populate_result(output_identifier_, result); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/node_operation.cc b/source/blender/compositor/realtime_compositor/intern/node_operation.cc new file mode 100644 index 00000000000..f02d0906447 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/node_operation.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_node_operation.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +NodeOperation::NodeOperation(Context &context, DNode node) : Operation(context), node_(node) +{ + for (const OutputSocketRef *output : node->outputs()) { + const ResultType result_type = get_node_socket_result_type(output); + const Result result = Result(result_type, texture_pool()); + populate_result(output->identifier(), result); + } + + for (const InputSocketRef *input : node->inputs()) { + const InputDescriptor input_descriptor = input_descriptor_from_input_socket(input); + declare_input_descriptor(input->identifier(), input_descriptor); + } +} + +void NodeOperation::compute_results_reference_counts(const Schedule &schedule) +{ + for (const OutputSocketRef *output_ref : node()->outputs()) { + const DOutputSocket output{node().context(), output_ref}; + + const int reference_count = number_of_inputs_linked_to_output_conditioned( + output, [&](DInputSocket input) { return schedule.contains(input.node()); }); + + get_result(output->identifier()).set_initial_reference_count(reference_count); + } +} + +const DNode &NodeOperation::node() const +{ + return node_; +} + +const bNode &NodeOperation::bnode() const +{ + return *node_->bnode(); +} + +bool NodeOperation::should_compute_output(StringRef identifier) +{ + return get_result(identifier).should_compute(); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/operation.cc b/source/blender/compositor/realtime_compositor/intern/operation.cc new file mode 100644 index 00000000000..42dd5aeebe8 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/operation.cc @@ -0,0 +1,201 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <limits> +#include <memory> + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "COM_context.hh" +#include "COM_conversion_operation.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_operation.hh" +#include "COM_realize_on_domain_operation.hh" +#include "COM_reduce_to_single_value_operation.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" +#include "COM_static_shader_manager.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Operation::Operation(Context &context) : context_(context) +{ +} + +Operation::~Operation() = default; + +void Operation::evaluate() +{ + evaluate_input_processors(); + + reset_results(); + + execute(); + + release_inputs(); +} + +Result &Operation::get_result(StringRef identifier) +{ + return results_.lookup(identifier); +} + +void Operation::map_input_to_result(StringRef identifier, Result *result) +{ + results_mapped_to_inputs_.add_new(identifier, result); +} + +Domain Operation::compute_domain() +{ + /* Default to an identity domain in case no domain input was found, most likely because all + * inputs are single values. */ + Domain operation_domain = Domain::identity(); + int current_domain_priority = std::numeric_limits<int>::max(); + + /* Go over the inputs and find the domain of the non single value input with the highest domain + * priority. */ + for (StringRef identifier : input_descriptors_.keys()) { + const Result &result = get_input(identifier); + const InputDescriptor &descriptor = get_input_descriptor(identifier); + + /* A single value input can't be a domain input. */ + if (result.is_single_value() || descriptor.expects_single_value) { + continue; + } + + /* Notice that the lower the domain priority value is, the higher the priority is, hence the + * less than comparison. */ + if (descriptor.domain_priority < current_domain_priority) { + operation_domain = result.domain(); + current_domain_priority = descriptor.domain_priority; + } + } + + return operation_domain; +} + +void Operation::add_and_evaluate_input_processors() +{ + /* Each input processor type is added to all inputs entirely before the next type. This is done + * because the construction of the input processors may depend on the result of previous input + * processors for all inputs. For instance, the realize on domain input processor considers the + * value of all inputs, so previous input processors for all inputs needs to be added and + * evaluated first. */ + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *single_value = ReduceToSingleValueOperation::construct_if_needed( + context(), get_input(identifier)); + add_and_evaluate_input_processor(identifier, single_value); + } + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *conversion = ConversionOperation::construct_if_needed( + context(), get_input(identifier), get_input_descriptor(identifier)); + add_and_evaluate_input_processor(identifier, conversion); + } + + for (const StringRef &identifier : results_mapped_to_inputs_.keys()) { + SimpleOperation *realize_on_domain = RealizeOnDomainOperation::construct_if_needed( + context(), get_input(identifier), get_input_descriptor(identifier), compute_domain()); + add_and_evaluate_input_processor(identifier, realize_on_domain); + } +} + +void Operation::add_and_evaluate_input_processor(StringRef identifier, SimpleOperation *processor) +{ + /* Allow null inputs to facilitate construct_if_needed pattern of addition. For instance, see the + * implementation of the add_and_evaluate_input_processors method. */ + if (!processor) { + return; + } + + ProcessorsVector &processors = input_processors_.lookup_or_add_default(identifier); + + /* Get the result that should serve as the input for the processor. This is either the result + * mapped to the input or the result of the last processor depending on whether this is the first + * processor or not. */ + Result &result = processors.is_empty() ? get_input(identifier) : processors.last()->get_result(); + + /* Map the input result of the processor and add it to the processors vector. */ + processor->map_input_to_result(&result); + processors.append(std::unique_ptr<SimpleOperation>(processor)); + + /* Switch the result mapped to the input to be the output result of the processor. */ + switch_result_mapped_to_input(identifier, &processor->get_result()); + + processor->evaluate(); +} + +Result &Operation::get_input(StringRef identifier) const +{ + return *results_mapped_to_inputs_.lookup(identifier); +} + +void Operation::switch_result_mapped_to_input(StringRef identifier, Result *result) +{ + results_mapped_to_inputs_.lookup(identifier) = result; +} + +void Operation::populate_result(StringRef identifier, Result result) +{ + results_.add_new(identifier, result); +} + +void Operation::declare_input_descriptor(StringRef identifier, InputDescriptor descriptor) +{ + input_descriptors_.add_new(identifier, descriptor); +} + +InputDescriptor &Operation::get_input_descriptor(StringRef identifier) +{ + return input_descriptors_.lookup(identifier); +} + +Context &Operation::context() +{ + return context_; +} + +TexturePool &Operation::texture_pool() const +{ + return context_.texture_pool(); +} + +StaticShaderManager &Operation::shader_manager() const +{ + return context_.shader_manager(); +} + +void Operation::evaluate_input_processors() +{ + if (!input_processors_added_) { + add_and_evaluate_input_processors(); + input_processors_added_ = true; + return; + } + + for (const ProcessorsVector &processors : input_processors_.values()) { + for (const std::unique_ptr<SimpleOperation> &processor : processors) { + processor->evaluate(); + } + } +} + +void Operation::reset_results() +{ + for (Result &result : results_.values()) { + result.reset(); + } +} + +void Operation::release_inputs() +{ + for (Result *result : results_mapped_to_inputs_.values()) { + result->release(); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc new file mode 100644 index 00000000000..47993060a74 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/realize_on_domain_operation.cc @@ -0,0 +1,130 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_utildefines.h" + +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_context.hh" +#include "COM_domain.hh" +#include "COM_input_descriptor.hh" +#include "COM_realize_on_domain_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +RealizeOnDomainOperation::RealizeOnDomainOperation(Context &context, + Domain domain, + ResultType type) + : SimpleOperation(context), domain_(domain) +{ + InputDescriptor input_descriptor; + input_descriptor.type = type; + declare_input_descriptor(input_descriptor); + populate_result(Result(type, texture_pool())); +} + +void RealizeOnDomainOperation::execute() +{ + Result &input = get_input(); + Result &result = get_result(); + + result.allocate_texture(domain_); + + GPUShader *shader = get_realization_shader(); + GPU_shader_bind(shader); + + /* Transform the input space into the domain space. */ + const float3x3 local_transformation = input.domain().transformation * + domain_.transformation.inverted(); + + /* Set the origin of the transformation to be the center of the domain. */ + const float3x3 transformation = float3x3::from_origin_transformation( + local_transformation, float2(domain_.size) / 2.0f); + + /* Invert the transformation because the shader transforms the domain coordinates instead of the + * input image itself and thus expect the inverse. */ + const float3x3 inverse_transformation = transformation.inverted(); + + GPU_shader_uniform_mat3_as_mat4(shader, "inverse_transformation", inverse_transformation.ptr()); + + /* The texture sampler should use bilinear interpolation for both the bilinear and bicubic + * cases, as the logic used by the bicubic realization shader expects textures to use bilinear + * interpolation. */ + const bool use_bilinear = ELEM(input.get_realization_options().interpolation, + Interpolation::Bilinear, + Interpolation::Bicubic); + GPU_texture_filter_mode(input.texture(), use_bilinear); + + /* Make out-of-bound texture access return zero by clamping to border color. And make texture + * wrap appropriately if the input repeats. */ + const bool repeats = input.get_realization_options().repeat_x || + input.get_realization_options().repeat_y; + GPU_texture_wrap_mode(input.texture(), repeats, false); + + input.bind_as_texture(shader, "input_tx"); + result.bind_as_image(shader, "domain_img"); + + compute_dispatch_threads_at_least(shader, domain_.size); + + input.unbind_as_texture(); + result.unbind_as_image(); + GPU_shader_unbind(); +} + +GPUShader *RealizeOnDomainOperation::get_realization_shader() +{ + switch (get_result().type()) { + case ResultType::Color: + return shader_manager().get("compositor_realize_on_domain_color"); + case ResultType::Vector: + return shader_manager().get("compositor_realize_on_domain_vector"); + case ResultType::Float: + return shader_manager().get("compositor_realize_on_domain_float"); + } + + BLI_assert_unreachable(); + return nullptr; +} + +Domain RealizeOnDomainOperation::compute_domain() +{ + return domain_; +} + +SimpleOperation *RealizeOnDomainOperation::construct_if_needed( + Context &context, + const Result &input_result, + const InputDescriptor &input_descriptor, + const Domain &operation_domain) +{ + /* This input wants to skip realization, the operation is not needed. */ + if (input_descriptor.skip_realization) { + return nullptr; + } + + /* The input expects a single value and if no single value is provided, it will be ignored and a + * default value will be used, so no need to realize it and the operation is not needed. */ + if (input_descriptor.expects_single_value) { + return nullptr; + } + + /* Input result is a single value and does not need realization, the operation is not needed. */ + if (input_result.is_single_value()) { + return nullptr; + } + + /* The input have an identical domain to the operation domain, so no need to realize it and the + * operation is not needed. */ + if (input_result.domain() == operation_domain) { + return nullptr; + } + + /* Otherwise, realization is needed. */ + return new RealizeOnDomainOperation(context, operation_domain, input_descriptor.type); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc new file mode 100644 index 00000000000..acc9b4ab7d6 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/reduce_to_single_value_operation.cc @@ -0,0 +1,67 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "MEM_guardedalloc.h" + +#include "COM_context.hh" +#include "COM_input_descriptor.hh" +#include "COM_reduce_to_single_value_operation.hh" +#include "COM_result.hh" + +namespace blender::realtime_compositor { + +ReduceToSingleValueOperation::ReduceToSingleValueOperation(Context &context, ResultType type) + : SimpleOperation(context) +{ + InputDescriptor input_descriptor; + input_descriptor.type = type; + declare_input_descriptor(input_descriptor); + populate_result(Result(type, texture_pool())); +} + +void ReduceToSingleValueOperation::execute() +{ + /* Make sure any prior writes to the texture are reflected before downloading it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + const Result &input = get_input(); + float *pixel = static_cast<float *>(GPU_texture_read(input.texture(), GPU_DATA_FLOAT, 0)); + + Result &result = get_result(); + result.allocate_single_value(); + switch (result.type()) { + case ResultType::Color: + result.set_color_value(pixel); + break; + case ResultType::Vector: + result.set_vector_value(pixel); + break; + case ResultType::Float: + result.set_float_value(*pixel); + break; + } + + MEM_freeN(pixel); +} + +SimpleOperation *ReduceToSingleValueOperation::construct_if_needed(Context &context, + const Result &input_result) +{ + /* Input result is already a single value, the operation is not needed. */ + if (input_result.is_single_value()) { + return nullptr; + } + + /* The input is a full sized texture and can't be reduced to a single value, the operation is not + * needed. */ + if (input_result.domain().size != int2(1)) { + return nullptr; + } + + /* The input is a texture of a single pixel and can be reduced to a single value. */ + return new ReduceToSingleValueOperation(context, input_result.type()); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/result.cc b/source/blender/compositor/realtime_compositor/intern/result.cc new file mode 100644 index 00000000000..8059367d211 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/result.cc @@ -0,0 +1,257 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_domain.hh" +#include "COM_result.hh" +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +Result::Result(ResultType type, TexturePool &texture_pool) + : type_(type), texture_pool_(&texture_pool) +{ +} + +void Result::allocate_texture(Domain domain) +{ + is_single_value_ = false; + switch (type_) { + case ResultType::Float: + texture_ = texture_pool_->acquire_float(domain.size); + break; + case ResultType::Vector: + texture_ = texture_pool_->acquire_vector(domain.size); + break; + case ResultType::Color: + texture_ = texture_pool_->acquire_color(domain.size); + break; + } + domain_ = domain; +} + +void Result::allocate_single_value() +{ + is_single_value_ = true; + /* Single values are stored in 1x1 textures as well as the single value members. */ + const int2 texture_size{1, 1}; + switch (type_) { + case ResultType::Float: + texture_ = texture_pool_->acquire_float(texture_size); + break; + case ResultType::Vector: + texture_ = texture_pool_->acquire_vector(texture_size); + break; + case ResultType::Color: + texture_ = texture_pool_->acquire_color(texture_size); + break; + } + domain_ = Domain::identity(); +} + +void Result::allocate_invalid() +{ + allocate_single_value(); + switch (type_) { + case ResultType::Float: + set_float_value(0.0f); + break; + case ResultType::Vector: + set_vector_value(float3(0.0f)); + break; + case ResultType::Color: + set_color_value(float4(0.0f)); + break; + } +} + +void Result::bind_as_texture(GPUShader *shader, const char *texture_name) const +{ + /* Make sure any prior writes to the texture are reflected before reading from it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH); + + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture_name); + GPU_texture_bind(texture_, texture_image_unit); +} + +void Result::bind_as_image(GPUShader *shader, const char *image_name) const +{ + const int image_unit = GPU_shader_get_texture_binding(shader, image_name); + GPU_texture_image_bind(texture_, image_unit); +} + +void Result::unbind_as_texture() const +{ + GPU_texture_unbind(texture_); +} + +void Result::unbind_as_image() const +{ + GPU_texture_image_unbind(texture_); +} + +void Result::pass_through(Result &target) +{ + /* Increment the reference count of the master by the original reference count of the target. */ + increment_reference_count(target.reference_count()); + + /* Make the target an exact copy of this result, but keep the initial reference count, as this is + * a property of the original result and is needed for correctly resetting the result before the + * next evaluation. */ + const int initial_reference_count = target.initial_reference_count_; + target = *this; + target.initial_reference_count_ = initial_reference_count; + + target.master_ = this; +} + +void Result::transform(const float3x3 &transformation) +{ + domain_.transform(transformation); +} + +RealizationOptions &Result::get_realization_options() +{ + return domain_.realization_options; +} + +float Result::get_float_value() const +{ + return float_value_; +} + +float3 Result::get_vector_value() const +{ + return vector_value_; +} + +float4 Result::get_color_value() const +{ + return color_value_; +} + +float Result::get_float_value_default(float default_value) const +{ + if (is_single_value()) { + return get_float_value(); + } + return default_value; +} + +float3 Result::get_vector_value_default(const float3 &default_value) const +{ + if (is_single_value()) { + return get_vector_value(); + } + return default_value; +} + +float4 Result::get_color_value_default(const float4 &default_value) const +{ + if (is_single_value()) { + return get_color_value(); + } + return default_value; +} + +void Result::set_float_value(float value) +{ + float_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, &float_value_); +} + +void Result::set_vector_value(const float3 &value) +{ + vector_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, vector_value_); +} + +void Result::set_color_value(const float4 &value) +{ + color_value_ = value; + GPU_texture_update(texture_, GPU_DATA_FLOAT, color_value_); +} + +void Result::set_initial_reference_count(int count) +{ + initial_reference_count_ = count; +} + +void Result::reset() +{ + master_ = nullptr; + reference_count_ = initial_reference_count_; +} + +void Result::increment_reference_count(int count) +{ + /* If there is a master result, increment its reference count instead. */ + if (master_) { + master_->increment_reference_count(count); + return; + } + + reference_count_ += count; +} + +void Result::release() +{ + /* If there is a master result, release it instead. */ + if (master_) { + master_->release(); + return; + } + + /* Decrement the reference count, and if it reaches zero, release the texture back into the + * texture pool. */ + reference_count_--; + if (reference_count_ == 0) { + texture_pool_->release(texture_); + } +} + +bool Result::should_compute() +{ + return initial_reference_count_ != 0; +} + +ResultType Result::type() const +{ + return type_; +} + +bool Result::is_texture() const +{ + return !is_single_value_; +} + +bool Result::is_single_value() const +{ + return is_single_value_; +} + +GPUTexture *Result::texture() const +{ + return texture_; +} + +int Result::reference_count() const +{ + /* If there is a master result, return its reference count instead. */ + if (master_) { + return master_->reference_count(); + } + return reference_count_; +} + +const Domain &Result::domain() const +{ + return domain_; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/scheduler.cc b/source/blender/compositor/realtime_compositor/intern/scheduler.cc new file mode 100644 index 00000000000..ce8b9330541 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/scheduler.cc @@ -0,0 +1,311 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" +#include "BLI_vector.hh" +#include "BLI_vector_set.hh" + +#include "NOD_derived_node_tree.hh" + +#include "COM_scheduler.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +/* Compute the output node whose result should be computed. The output node is the node marked as + * NODE_DO_OUTPUT. If multiple types of output nodes are marked, then the preference will be + * CMP_NODE_COMPOSITE > CMP_NODE_VIEWER > CMP_NODE_SPLITVIEWER. If no output node exists, a null + * node will be returned. */ +static DNode compute_output_node(DerivedNodeTree &tree) +{ + const NodeTreeRef &root_tree = tree.root_context().tree(); + + for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeComposite")) { + if (node->bnode()->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeViewer")) { + if (node->bnode()->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + for (const NodeRef *node : root_tree.nodes_by_type("CompositorNodeSplitViewer")) { + if (node->bnode()->flag & NODE_DO_OUTPUT) { + return DNode(&tree.root_context(), node); + } + } + + /* No output node found, return a null node. */ + return DNode(); +} + +/* A type representing a mapping that associates each node with a heuristic estimation of the + * number of intermediate buffers needed to compute it and all of its dependencies. See the + * compute_number_of_needed_buffers function for more information. */ +using NeededBuffers = Map<DNode, int>; + +/* Compute a heuristic estimation of the number of intermediate buffers needed to compute each node + * and all of its dependencies for all nodes that the given node depends on. The output is a map + * that maps each node with the number of intermediate buffers needed to compute it and all of its + * dependencies. + * + * Consider a node that takes n number of buffers as an input from a number of node dependencies, + * which we shall call the input nodes. The node also computes and outputs m number of buffers. + * In order for the node to compute its output, a number of intermediate buffers will be needed. + * Since the node takes n buffers and outputs m buffers, then the number of buffers directly + * needed by the node is (n + m). But each of the input buffers are computed by a node that, in + * turn, needs a number of buffers to compute its output. So the total number of buffers needed + * to compute the output of the node is max(n + m, d) where d is the number of buffers needed by + * the input node that needs the largest number of buffers. We only consider the input node that + * needs the largest number of buffers, because those buffers can be reused by any input node + * that needs a lesser number of buffers. + * + * Shader nodes, however, are a special case because links between two shader nodes inside the same + * shader operation don't pass a buffer, but a single value in the compiled shader. So for shader + * nodes, only inputs and outputs linked to nodes that are not shader nodes should be considered. + * Note that this might not actually be true, because the compiler may decide to split a shader + * operation into multiples ones that will pass buffers, but this is not something that can be + * known at scheduling-time. See the discussion in COM_compile_state.hh, COM_evaluator.hh, and + * COM_shader_operation.hh for more information. In the node tree shown below, node 4 will have + * exactly the same number of needed buffers by node 3, because its inputs and outputs are all + * internally linked in the shader operation. + * + * Shader Operation + * +------------------------------------------------------+ + * .------------. | .------------. .------------. .------------. | .------------. + * | Node 1 | | | Node 3 | | Node 4 | | Node 5 | | | Node 6 | + * | |----|--| |--| |------| |--|--| | + * | | .-|--| | | | .---| | | | | + * '------------' | | '------------' '------------' | '------------' | '------------' + * | +----------------------------------|-------------------+ + * .------------. | | + * | Node 2 | | | + * | |--'------------------------------------' + * | | + * '------------' + * + * Note that the computed output is not guaranteed to be accurate, and will not be in most cases. + * The computation is merely a heuristic estimation that works well in most cases. This is due to a + * number of reasons: + * - The node tree is actually a graph that allows output sharing, which is not something that was + * taken into consideration in this implementation because it is difficult to correctly consider. + * - Each node may allocate any number of internal buffers, which is not taken into account in this + * implementation because it rarely affects the output and is done by very few nodes. + * - The compiler may decide to compiler the schedule differently depending on runtime information + * which we can merely speculate at scheduling-time as described above. */ +static NeededBuffers compute_number_of_needed_buffers(DNode output_node) +{ + NeededBuffers needed_buffers; + + /* A stack of nodes used to traverse the node tree starting from the output node. */ + Stack<DNode> node_stack = {output_node}; + + /* Traverse the node tree in a post order depth first manner and compute the number of needed + * buffers for each node. Post order traversal guarantee that all the node dependencies of each + * node are computed before it. This is done by pushing all the uncomputed node dependencies to + * the node stack first and only popping and computing the node when all its node dependencies + * were computed. */ + while (!node_stack.is_empty()) { + /* Do not pop the node immediately, as it may turn out that we can't compute its number of + * needed buffers just yet because its dependencies weren't computed, it will be popped later + * when needed. */ + DNode &node = node_stack.peek(); + + /* Go over the node dependencies connected to the inputs of the node and push them to the node + * stack if they were not computed already. */ + Set<DNode> pushed_nodes; + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked and + * has no dependency node. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + /* The node dependency was already computed or pushed before, so skip it. */ + if (needed_buffers.contains(output.node()) || pushed_nodes.contains(output.node())) { + continue; + } + + /* The output node needs to be computed, push the node dependency to the node stack and + * indicate that it was pushed. */ + node_stack.push(output.node()); + pushed_nodes.add_new(output.node()); + } + + /* If any of the node dependencies were pushed, that means that not all of them were computed + * and consequently we can't compute the number of needed buffers for this node just yet. */ + if (!pushed_nodes.is_empty()) { + continue; + } + + /* We don't need to store the result of the pop because we already peeked at it before. */ + node_stack.pop(); + + /* Compute the number of buffers that the node takes as an input as well as the number of + * buffers needed to compute the most demanding of the node dependencies. */ + int number_of_input_buffers = 0; + int buffers_needed_by_dependencies = 0; + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked. + * Unlinked inputs do not take a buffer, so skip those inputs. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + /* Since this input is linked, if the link is not between two shader nodes, it means that the + * node takes a buffer through this input and so we increment the number of input buffers. */ + if (!is_shader_node(node) || !is_shader_node(output.node())) { + number_of_input_buffers++; + } + + /* If the number of buffers needed by the node dependency is more than the total number of + * buffers needed by the dependencies, then update the latter to be the former. This is + * computing the "d" in the aforementioned equation "max(n + m, d)". */ + const int buffers_needed_by_dependency = needed_buffers.lookup(output.node()); + if (buffers_needed_by_dependency > buffers_needed_by_dependencies) { + buffers_needed_by_dependencies = buffers_needed_by_dependency; + } + } + + /* Compute the number of buffers that will be computed/output by this node. */ + int number_of_output_buffers = 0; + for (const OutputSocketRef *output_ref : node->outputs()) { + const DOutputSocket output{node.context(), output_ref}; + + /* The output is not linked, it outputs no buffer. */ + if (output->logically_linked_sockets().is_empty()) { + continue; + } + + /* If any of the links is not between two shader nodes, it means that the node outputs + * a buffer through this output and so we increment the number of output buffers. */ + if (!is_output_linked_to_node_conditioned(output, is_shader_node) || !is_shader_node(node)) { + number_of_output_buffers++; + } + } + + /* Compute the heuristic estimation of the number of needed intermediate buffers to compute + * this node and all of its dependencies. This is computing the aforementioned equation + * "max(n + m, d)". */ + const int total_buffers = MAX2(number_of_input_buffers + number_of_output_buffers, + buffers_needed_by_dependencies); + needed_buffers.add(node, total_buffers); + } + + return needed_buffers; +} + +/* There are multiple different possible orders of evaluating a node graph, each of which needs + * to allocate a number of intermediate buffers to store its intermediate results. It follows + * that we need to find the evaluation order which uses the least amount of intermediate buffers. + * For instance, consider a node that takes two input buffers A and B. Each of those buffers is + * computed through a number of nodes constituting a sub-graph whose root is the node that + * outputs that buffer. Suppose the number of intermediate buffers needed to compute A and B are + * N(A) and N(B) respectively and N(A) > N(B). Then evaluating the sub-graph computing A would be + * a better option than that of B, because had B was computed first, its outputs will need to be + * stored in extra buffers in addition to the buffers needed by A. The number of buffers needed by + * each node is estimated as described in the compute_number_of_needed_buffers function. + * + * This is a heuristic generalization of the Sethi–Ullman algorithm, a generalization that + * doesn't always guarantee an optimal evaluation order, as the optimal evaluation order is very + * difficult to compute, however, this method works well in most cases. Moreover it assumes that + * all buffers will have roughly the same size, which may not always be the case. */ +Schedule compute_schedule(DerivedNodeTree &tree) +{ + Schedule schedule; + + /* Compute the output node whose result should be computed. */ + const DNode output_node = compute_output_node(tree); + + /* No output node, the node tree has no effect, return an empty schedule. */ + if (!output_node) { + return schedule; + } + + /* Compute the number of buffers needed by each node connected to the output. */ + const NeededBuffers needed_buffers = compute_number_of_needed_buffers(output_node); + + /* A stack of nodes used to traverse the node tree starting from the output node. */ + Stack<DNode> node_stack = {output_node}; + + /* Traverse the node tree in a post order depth first manner, scheduling the nodes in an order + * informed by the number of buffers needed by each node. Post order traversal guarantee that all + * the node dependencies of each node are scheduled before it. This is done by pushing all the + * unscheduled node dependencies to the node stack first and only popping and scheduling the node + * when all its node dependencies were scheduled. */ + while (!node_stack.is_empty()) { + /* Do not pop the node immediately, as it may turn out that we can't schedule it just yet + * because its dependencies weren't scheduled, it will be popped later when needed. */ + DNode &node = node_stack.peek(); + + /* Compute the nodes directly connected to the node inputs sorted by their needed buffers such + * that the node with the lowest number of needed buffers comes first. Note that we actually + * want the node with the highest number of needed buffers to be schedule first, but since + * those are pushed to the traversal stack, we need to push them in reverse order. */ + Vector<DNode> sorted_dependency_nodes; + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked and + * has no dependency node, so skip it. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + /* The dependency node was added before, so skip it. The number of dependency nodes is very + * small, typically less than 3, so a linear search is okay. */ + if (sorted_dependency_nodes.contains(output.node())) { + continue; + } + + /* The dependency node was already schedule, so skip it. */ + if (schedule.contains(output.node())) { + continue; + } + + /* Sort in ascending order on insertion, the number of dependency nodes is very small, + * typically less than 3, so insertion sort is okay. */ + int insertion_position = 0; + for (int i = 0; i < sorted_dependency_nodes.size(); i++) { + if (needed_buffers.lookup(output.node()) > + needed_buffers.lookup(sorted_dependency_nodes[i])) { + insertion_position++; + } + else { + break; + } + } + sorted_dependency_nodes.insert(insertion_position, output.node()); + } + + /* Push the sorted dependency nodes to the node stack in order. */ + for (const DNode &dependency_node : sorted_dependency_nodes) { + node_stack.push(dependency_node); + } + + /* If there are no sorted dependency nodes, that means they were all already scheduled or that + * none exists in the first place, so we can pop and schedule the node now. */ + if (sorted_dependency_nodes.is_empty()) { + /* The node might have already been scheduled, so we don't use add_new here and simply don't + * add it if it was already scheduled. */ + schedule.add(node_stack.pop()); + } + } + + return schedule; +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/shader_node.cc b/source/blender/compositor/realtime_compositor/intern/shader_node.cc new file mode 100644 index 00000000000..f23485cee96 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/shader_node.cc @@ -0,0 +1,155 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_assert.h" +#include "BLI_math_vector.h" +#include "BLI_string_ref.hh" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +ShaderNode::ShaderNode(DNode node) : node_(node) +{ + populate_inputs(); + populate_outputs(); +} + +GPUNodeStack *ShaderNode::get_inputs_array() +{ + return inputs_.data(); +} + +GPUNodeStack *ShaderNode::get_outputs_array() +{ + return outputs_.data(); +} + +GPUNodeStack &ShaderNode::get_input(StringRef identifier) +{ + return inputs_[node_.input_by_identifier(identifier)->index()]; +} + +GPUNodeStack &ShaderNode::get_output(StringRef identifier) +{ + return outputs_[node_.output_by_identifier(identifier)->index()]; +} + +GPUNodeLink *ShaderNode::get_input_link(StringRef identifier) +{ + GPUNodeStack &input = get_input(identifier); + if (input.link) { + return input.link; + } + return GPU_uniform(input.vec); +} + +const DNode &ShaderNode::node() const +{ + return node_; +} + +bNode &ShaderNode::bnode() const +{ + return *node_->bnode(); +} + +static eGPUType gpu_type_from_socket_type(eNodeSocketDatatype type) +{ + switch (type) { + case SOCK_FLOAT: + return GPU_FLOAT; + case SOCK_VECTOR: + return GPU_VEC3; + case SOCK_RGBA: + return GPU_VEC4; + default: + BLI_assert_unreachable(); + return GPU_NONE; + } +} + +static void gpu_stack_vector_from_socket(float *vector, const SocketRef *socket) +{ + switch (socket->bsocket()->type) { + case SOCK_FLOAT: + vector[0] = socket->default_value<bNodeSocketValueFloat>()->value; + return; + case SOCK_VECTOR: + copy_v3_v3(vector, socket->default_value<bNodeSocketValueVector>()->value); + return; + case SOCK_RGBA: + copy_v4_v4(vector, socket->default_value<bNodeSocketValueRGBA>()->value); + return; + default: + BLI_assert_unreachable(); + } +} + +static void populate_gpu_node_stack(DSocket socket, GPUNodeStack &stack) +{ + /* Make sure this stack is not marked as the end of the stack array. */ + stack.end = false; + /* This will be initialized later by the GPU material compiler or the compile method. */ + stack.link = nullptr; + + stack.sockettype = socket->bsocket()->type; + stack.type = gpu_type_from_socket_type((eNodeSocketDatatype)socket->bsocket()->type); + + if (socket->is_input()) { + const DInputSocket input(socket); + + DSocket origin = get_input_origin_socket(input); + + /* The input is linked if the origin socket is an output socket. Had it been an input socket, + * then it is an unlinked input of a group input node. */ + stack.hasinput = origin->is_output(); + + /* Get the socket value from the origin if it is an input, because then it would either be an + * unlinked input or an unlinked input of a group input node that the socket is linked to, + * otherwise, get the value from the socket itself. */ + if (origin->is_input()) { + gpu_stack_vector_from_socket(stack.vec, origin.socket_ref()); + } + else { + gpu_stack_vector_from_socket(stack.vec, socket.socket_ref()); + } + } + else { + stack.hasoutput = socket->is_logically_linked(); + } +} + +void ShaderNode::populate_inputs() +{ + /* Reserve a stack for each input in addition to an extra stack at the end to mark the end of the + * array, as this is what the GPU module functions expect. */ + inputs_.resize(node_->inputs().size() + 1); + inputs_.last().end = true; + + for (int i = 0; i < node_->inputs().size(); i++) { + populate_gpu_node_stack(node_.input(i), inputs_[i]); + } +} + +void ShaderNode::populate_outputs() +{ + /* Reserve a stack for each output in addition to an extra stack at the end to mark the end of + * the array, as this is what the GPU module functions expect. */ + outputs_.resize(node_->outputs().size() + 1); + outputs_.last().end = true; + + for (int i = 0; i < node_->outputs().size(); i++) { + populate_gpu_node_stack(node_.output(i), outputs_[i]); + } +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/shader_operation.cc b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc new file mode 100644 index 00000000000..a097c81a4c5 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/shader_operation.cc @@ -0,0 +1,522 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <memory> +#include <string> + +#include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_utildefines.h" + +#include "DNA_customdata_types.h" + +#include "GPU_material.h" +#include "GPU_shader.h" +#include "GPU_texture.h" +#include "GPU_uniform_buffer.h" + +#include "gpu_shader_create_info.hh" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "COM_context.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_scheduler.hh" +#include "COM_shader_node.hh" +#include "COM_shader_operation.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; + +ShaderOperation::ShaderOperation(Context &context, ShaderCompileUnit &compile_unit) + : Operation(context), compile_unit_(compile_unit) +{ + material_ = GPU_material_from_callbacks(&construct_material, &generate_code, this); + GPU_material_status_set(material_, GPU_MAT_QUEUED); + GPU_material_compile(material_); +} + +ShaderOperation::~ShaderOperation() +{ + GPU_material_free_single(material_); +} + +void ShaderOperation::execute() +{ + const Domain domain = compute_domain(); + for (StringRef identifier : output_sockets_to_output_identifiers_map_.values()) { + Result &result = get_result(identifier); + result.allocate_texture(domain); + } + + GPUShader *shader = GPU_material_get_shader(material_); + GPU_shader_bind(shader); + + bind_material_resources(shader); + bind_inputs(shader); + bind_outputs(shader); + + compute_dispatch_threads_at_least(shader, domain.size); + + GPU_texture_unbind_all(); + GPU_texture_image_unbind_all(); + GPU_uniformbuf_unbind_all(); + GPU_shader_unbind(); +} + +StringRef ShaderOperation::get_output_identifier_from_output_socket(DOutputSocket output_socket) +{ + return output_sockets_to_output_identifiers_map_.lookup(output_socket); +} + +Map<std::string, DOutputSocket> &ShaderOperation::get_inputs_to_linked_outputs_map() +{ + return inputs_to_linked_outputs_map_; +} + +void ShaderOperation::compute_results_reference_counts(const Schedule &schedule) +{ + for (const auto &item : output_sockets_to_output_identifiers_map_.items()) { + const int reference_count = number_of_inputs_linked_to_output_conditioned( + item.key, [&](DInputSocket input) { return schedule.contains(input.node()); }); + + get_result(item.value).set_initial_reference_count(reference_count); + } +} + +void ShaderOperation::bind_material_resources(GPUShader *shader) +{ + /* Bind the uniform buffer of the material if it exists. It may not exist if the GPU material has + * no uniforms. */ + GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material_); + if (ubo) { + GPU_uniformbuf_bind(ubo, GPU_shader_get_uniform_block_binding(shader, GPU_UBO_BLOCK_NAME)); + } + + /* Bind color band textures needed by curve and ramp nodes. */ + ListBase textures = GPU_material_textures(material_); + LISTBASE_FOREACH (GPUMaterialTexture *, texture, &textures) { + if (texture->colorband) { + const int texture_image_unit = GPU_shader_get_texture_binding(shader, texture->sampler_name); + GPU_texture_bind(*texture->colorband, texture_image_unit); + } + } +} + +void ShaderOperation::bind_inputs(GPUShader *shader) +{ + /* Attributes represents the inputs of the operation and their names match those of the inputs of + * the operation as well as the corresponding texture samples in the shader. */ + ListBase attributes = GPU_material_attributes(material_); + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + get_input(attribute->name).bind_as_texture(shader, attribute->name); + } +} + +void ShaderOperation::bind_outputs(GPUShader *shader) +{ + for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) { + get_result(output_identifier).bind_as_image(shader, output_identifier.c_str()); + } +} + +void ShaderOperation::construct_material(void *thunk, GPUMaterial *material) +{ + ShaderOperation *operation = static_cast<ShaderOperation *>(thunk); + for (DNode node : operation->compile_unit_) { + ShaderNode *shader_node = node->typeinfo()->get_compositor_shader_node(node); + operation->shader_nodes_.add_new(node, std::unique_ptr<ShaderNode>(shader_node)); + + operation->link_node_inputs(node, material); + + shader_node->compile(material); + + operation->populate_results_for_node(node, material); + } +} + +void ShaderOperation::link_node_inputs(DNode node, GPUMaterial *material) +{ + for (const InputSocketRef *input_ref : node->inputs()) { + const DInputSocket input{node.context(), input_ref}; + + /* Get the output linked to the input. If it is null, that means the input is unlinked. + * Unlinked inputs are linked by the node compile method, so skip this here. */ + const DOutputSocket output = get_output_linked_to_input(input); + if (!output) { + continue; + } + + /* If the origin node is part of the shader operation, then the link is internal to the GPU + * material graph and is linked appropriately. */ + if (compile_unit_.contains(output.node())) { + link_node_input_internal(input, output); + continue; + } + + /* Otherwise, the origin node is not part of the shader operation, then the link is external to + * the GPU material graph and an input to the shader operation must be declared and linked to + * the node input. */ + link_node_input_external(input, output, material); + } +} + +void ShaderOperation::link_node_input_internal(DInputSocket input_socket, + DOutputSocket output_socket) +{ + ShaderNode &output_node = *shader_nodes_.lookup(output_socket.node()); + GPUNodeStack &output_stack = output_node.get_output(output_socket->identifier()); + + ShaderNode &input_node = *shader_nodes_.lookup(input_socket.node()); + GPUNodeStack &input_stack = input_node.get_input(input_socket->identifier()); + + input_stack.link = output_stack.link; +} + +void ShaderOperation::link_node_input_external(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material) +{ + + ShaderNode &node = *shader_nodes_.lookup(input_socket.node()); + GPUNodeStack &stack = node.get_input(input_socket->identifier()); + + /* An input was already declared for that same output socket, so no need to declare it again. */ + if (!output_to_material_attribute_map_.contains(output_socket)) { + declare_operation_input(input_socket, output_socket, material); + } + + /* Link the attribute representing the shader operation input corresponding to the given output + * socket. */ + stack.link = output_to_material_attribute_map_.lookup(output_socket); +} + +static const char *get_set_function_name(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "set_value"; + case ResultType::Vector: + return "set_rgb"; + case ResultType::Color: + return "set_rgba"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::declare_operation_input(DInputSocket input_socket, + DOutputSocket output_socket, + GPUMaterial *material) +{ + const int input_index = output_to_material_attribute_map_.size(); + std::string input_identifier = "input" + std::to_string(input_index); + + /* Declare the input descriptor for this input and prefer to declare its type to be the same as + * the type of the output socket because doing type conversion in the shader is much cheaper. */ + InputDescriptor input_descriptor = input_descriptor_from_input_socket(input_socket.socket_ref()); + input_descriptor.type = get_node_socket_result_type(output_socket.socket_ref()); + declare_input_descriptor(input_identifier, input_descriptor); + + /* Add a new GPU attribute representing an input to the GPU material. Instead of using the + * attribute directly, we link it to an appropriate set function and use its output link instead. + * This is needed because the `gputype` member of the attribute is only initialized if it is + * linked to a GPU node. */ + GPUNodeLink *attribute_link; + GPU_link(material, + get_set_function_name(input_descriptor.type), + GPU_attribute(material, CD_AUTO_FROM_NAME, input_identifier.c_str()), + &attribute_link); + + /* Map the output socket to the attribute that was created for it. */ + output_to_material_attribute_map_.add(output_socket, attribute_link); + + /* Map the identifier of the operation input to the output socket it is linked to. */ + inputs_to_linked_outputs_map_.add_new(input_identifier, output_socket); +} + +void ShaderOperation::populate_results_for_node(DNode node, GPUMaterial *material) +{ + for (const OutputSocketRef *output_ref : node->outputs()) { + const DOutputSocket output{node.context(), output_ref}; + + /* If any of the nodes linked to the output are not part of the shader operation, then an + * output result needs to be populated for it. */ + const bool need_to_populate_result = is_output_linked_to_node_conditioned( + output, [&](DNode node) { return !compile_unit_.contains(node); }); + + if (need_to_populate_result) { + populate_operation_result(output, material); + } + } +} + +static const char *get_store_function_name(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "node_compositor_store_output_float"; + case ResultType::Vector: + return "node_compositor_store_output_vector"; + case ResultType::Color: + return "node_compositor_store_output_color"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::populate_operation_result(DOutputSocket output_socket, GPUMaterial *material) +{ + const unsigned int output_id = output_sockets_to_output_identifiers_map_.size(); + std::string output_identifier = "output" + std::to_string(output_id); + + const ResultType result_type = get_node_socket_result_type(output_socket.socket_ref()); + const Result result = Result(result_type, texture_pool()); + populate_result(output_identifier, result); + + /* Map the output socket to the identifier of the newly populated result. */ + output_sockets_to_output_identifiers_map_.add_new(output_socket, output_identifier); + + ShaderNode &node = *shader_nodes_.lookup(output_socket.node()); + GPUNodeLink *output_link = node.get_output(output_socket->identifier()).link; + + /* Link the output node stack to an output storer storing in the appropriate result. The result + * is identified by its index in the operation and the index is encoded as a float to be passed + * to the GPU function. Additionally, create an output link from the storer node to declare as an + * output to the GPU material. This storer output link is a dummy link in the sense that its + * value is ignored since it is already written in the output, but it is used to track nodes that + * contribute to the output of the compositor node tree. */ + GPUNodeLink *storer_output_link; + GPUNodeLink *id_link = GPU_constant((float *)&output_id); + const char *store_function_name = get_store_function_name(result_type); + GPU_link(material, store_function_name, id_link, output_link, &storer_output_link); + + /* Declare the output link of the storer node as an output of the GPU material to help the GPU + * code generator to track the nodes that contribute to the output of the shader. */ + GPU_material_add_output_link_composite(material, storer_output_link); +} + +using namespace gpu::shader; + +void ShaderOperation::generate_code(void *thunk, + GPUMaterial *material, + GPUCodegenOutput *code_generator_output) +{ + ShaderOperation *operation = static_cast<ShaderOperation *>(thunk); + ShaderCreateInfo &shader_create_info = *reinterpret_cast<ShaderCreateInfo *>( + code_generator_output->create_info); + + shader_create_info.local_group_size(16, 16); + + /* The resources are added without explicit locations, so make sure it is done by the + * shader creator. */ + shader_create_info.auto_resource_location(true); + + /* Add implementation for implicit conversion operations inserted by the code generator. This + * file should include the functions [float|vec3|vec4]_from_[float|vec3|vec4]. */ + shader_create_info.typedef_source("gpu_shader_compositor_type_conversion.glsl"); + + /* The source shader is a compute shader with a main function that calls the dynamically + * generated evaluate function. The evaluate function includes the serialized GPU material graph + * preceded by code that initialized the inputs of the operation. Additionally, the storer + * functions that writes the outputs are defined outside the evaluate function. */ + shader_create_info.compute_source("gpu_shader_compositor_main.glsl"); + + /* The main function is emitted in the shader before the evaluate function, so the evaluate + * function needs to be forward declared here. */ + shader_create_info.typedef_source_generated += "void evaluate();\n"; + + operation->generate_code_for_outputs(shader_create_info); + + shader_create_info.compute_source_generated += "void evaluate()\n{\n"; + + operation->generate_code_for_inputs(material, shader_create_info); + + shader_create_info.compute_source_generated += code_generator_output->composite; + + shader_create_info.compute_source_generated += "}\n"; +} + +static eGPUTextureFormat texture_format_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return GPU_R16F; + case ResultType::Vector: + return GPU_RGBA16F; + case ResultType::Color: + return GPU_RGBA16F; + } + + BLI_assert_unreachable(); + return GPU_RGBA16F; +} + +/* Texture storers in the shader always take a vec4 as an argument, so encode each type in a vec4 + * appropriately. */ +static const char *glsl_store_expression_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "vec4(value)"; + case ResultType::Vector: + return "vec4(vector, 0.0)"; + case ResultType::Color: + return "color"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::generate_code_for_outputs(ShaderCreateInfo &shader_create_info) +{ + const std::string store_float_function_header = "void store_float(const uint id, float value)"; + const std::string store_vector_function_header = "void store_vector(const uint id, vec3 vector)"; + const std::string store_color_function_header = "void store_color(const uint id, vec4 color)"; + + /* The store functions are used by the node_compositor_store_output_[float|vector|color] + * functions but are only defined later as part of the compute source, so they need to be forward + * declared. */ + shader_create_info.typedef_source_generated += store_float_function_header + ";\n"; + shader_create_info.typedef_source_generated += store_vector_function_header + ";\n"; + shader_create_info.typedef_source_generated += store_color_function_header + ";\n"; + + /* Each of the store functions is essentially a single switch case on the given ID, so start by + * opening the function with a curly bracket followed by opening a switch statement in each of + * the functions. */ + std::stringstream store_float_function; + std::stringstream store_vector_function; + std::stringstream store_color_function; + const std::string store_function_start = "\n{\n switch (id) {\n"; + store_float_function << store_float_function_header << store_function_start; + store_vector_function << store_vector_function_header << store_function_start; + store_color_function << store_color_function_header << store_function_start; + + for (StringRefNull output_identifier : output_sockets_to_output_identifiers_map_.values()) { + const Result &result = get_result(output_identifier); + + /* Add a write-only image for this output where its values will be written. */ + shader_create_info.image(0, + texture_format_from_result_type(result.type()), + Qualifier::WRITE, + ImageType::FLOAT_2D, + output_identifier, + Frequency::BATCH); + + /* Add a case for the index of this output followed by a break statement. */ + std::stringstream case_code; + const std::string store_expression = glsl_store_expression_from_result_type(result.type()); + const std::string texel = ", ivec2(gl_GlobalInvocationID.xy), "; + case_code << " case " << StringRef(output_identifier).drop_known_prefix("output") << ":\n" + << " imageStore(" << output_identifier << texel << store_expression << ");\n" + << " break;\n"; + + /* Only add the case to the function with the matching type. */ + switch (result.type()) { + case ResultType::Float: + store_float_function << case_code.str(); + break; + case ResultType::Vector: + store_vector_function << case_code.str(); + break; + case ResultType::Color: + store_color_function << case_code.str(); + break; + } + } + + /* Close the previously opened switch statement as well as the function itself. */ + const std::string store_function_end = " }\n}\n\n"; + store_float_function << store_function_end; + store_vector_function << store_function_end; + store_color_function << store_function_end; + + shader_create_info.compute_source_generated += store_float_function.str() + + store_vector_function.str() + + store_color_function.str(); +} + +static const char *glsl_type_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "float"; + case ResultType::Vector: + return "vec3"; + case ResultType::Color: + return "vec4"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +/* Texture loaders in the shader always return a vec4, so a swizzle is needed to retrieve the + * actual value for each type. */ +static const char *glsl_swizzle_from_result_type(ResultType type) +{ + switch (type) { + case ResultType::Float: + return "x"; + case ResultType::Vector: + return "xyz"; + case ResultType::Color: + return "rgba"; + } + + BLI_assert_unreachable(); + return nullptr; +} + +void ShaderOperation::generate_code_for_inputs(GPUMaterial *material, + ShaderCreateInfo &shader_create_info) +{ + /* The attributes of the GPU material represents the inputs of the operation. */ + ListBase attributes = GPU_material_attributes(material); + + /* Add a texture sampler for each of the inputs with the same name as the attribute. */ + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + shader_create_info.sampler(0, ImageType::FLOAT_2D, attribute->name, Frequency::BATCH); + } + + /* Declare a struct called var_attrs that includes an appropriately typed member for each of the + * inputs. The names of the members should be the letter v followed by the ID of the attribute + * corresponding to the input. Such names are expected by the code generator. */ + std::stringstream declare_attributes; + declare_attributes << "struct {\n"; + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name); + const std::string type = glsl_type_from_result_type(input_descriptor.type); + declare_attributes << " " << type << " v" << attribute->id << ";\n"; + } + declare_attributes << "} var_attrs;\n\n"; + + shader_create_info.compute_source_generated += declare_attributes.str(); + + /* The texture loader utilities are needed to sample the input textures and initialize the + * attributes. */ + shader_create_info.typedef_source("gpu_shader_compositor_texture_utilities.glsl"); + + /* Initialize each member of the previously declared struct by loading its corresponding texture + * with an appropriate swizzle for its type. */ + std::stringstream initialize_attributes; + LISTBASE_FOREACH (GPUMaterialAttribute *, attribute, &attributes) { + const InputDescriptor &input_descriptor = get_input_descriptor(attribute->name); + const std::string swizzle = glsl_swizzle_from_result_type(input_descriptor.type); + initialize_attributes << "var_attrs.v" << attribute->id << " = " + << "texture_load(" << attribute->name + << ", ivec2(gl_GlobalInvocationID.xy))." << swizzle << ";\n"; + } + initialize_attributes << "\n"; + + shader_create_info.compute_source_generated += initialize_attributes.str(); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/simple_operation.cc b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc new file mode 100644 index 00000000000..d55a20e5c54 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/simple_operation.cc @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "COM_input_descriptor.hh" +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_simple_operation.hh" + +namespace blender::realtime_compositor { + +const StringRef SimpleOperation::input_identifier_ = StringRef("Input"); +const StringRef SimpleOperation::output_identifier_ = StringRef("Output"); + +Result &SimpleOperation::get_result() +{ + return Operation::get_result(output_identifier_); +} + +void SimpleOperation::map_input_to_result(Result *result) +{ + Operation::map_input_to_result(input_identifier_, result); +} + +void SimpleOperation::add_and_evaluate_input_processors() +{ +} + +Result &SimpleOperation::get_input() +{ + return Operation::get_input(input_identifier_); +} + +void SimpleOperation::switch_result_mapped_to_input(Result *result) +{ + Operation::switch_result_mapped_to_input(input_identifier_, result); +} + +void SimpleOperation::populate_result(Result result) +{ + Operation::populate_result(output_identifier_, result); + + /* The result of a simple operation is guaranteed to have a single user. */ + get_result().set_initial_reference_count(1); +} + +void SimpleOperation::declare_input_descriptor(InputDescriptor descriptor) +{ + Operation::declare_input_descriptor(input_identifier_, descriptor); +} + +InputDescriptor &SimpleOperation::get_input_descriptor() +{ + return Operation::get_input_descriptor(input_identifier_); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc new file mode 100644 index 00000000000..c9c8a056f87 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/static_shader_manager.cc @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "GPU_shader.h" + +#include "COM_static_shader_manager.hh" + +namespace blender::realtime_compositor { + +StaticShaderManager::~StaticShaderManager() +{ + for (GPUShader *shader : shaders_.values()) { + GPU_shader_free(shader); + } +} + +GPUShader *StaticShaderManager::get(const char *info_name) +{ + /* If a shader with the same info name already exists in the manager, return it, otherwise, + * create a new shader from the info name and return it. */ + return shaders_.lookup_or_add_cb( + info_name, [info_name]() { return GPU_shader_create_from_info_name(info_name); }); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/texture_pool.cc b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc new file mode 100644 index 00000000000..1568970a030 --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/texture_pool.cc @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include <cstdint> + +#include "BLI_hash.hh" +#include "BLI_map.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_vector.hh" + +#include "GPU_texture.h" + +#include "COM_texture_pool.hh" + +namespace blender::realtime_compositor { + +/* -------------------------------------------------------------------- + * Texture Pool Key. + */ + +TexturePoolKey::TexturePoolKey(int2 size, eGPUTextureFormat format) : size(size), format(format) +{ +} + +TexturePoolKey::TexturePoolKey(const GPUTexture *texture) +{ + size = int2(GPU_texture_width(texture), GPU_texture_height(texture)); + format = GPU_texture_format(texture); +} + +uint64_t TexturePoolKey::hash() const +{ + return get_default_hash_3(size.x, size.y, format); +} + +bool operator==(const TexturePoolKey &a, const TexturePoolKey &b) +{ + return a.size == b.size && a.format == b.format; +} + +/* -------------------------------------------------------------------- + * Texture Pool. + */ + +GPUTexture *TexturePool::acquire(int2 size, eGPUTextureFormat format) +{ + /* Check if there is an available texture with the required specification, and if one exists, + * return it. */ + const TexturePoolKey key = TexturePoolKey(size, format); + Vector<GPUTexture *> &available_textures = textures_.lookup_or_add_default(key); + if (!available_textures.is_empty()) { + return available_textures.pop_last(); + } + + /* Otherwise, allocate a new texture. */ + return allocate_texture(size, format); +} + +GPUTexture *TexturePool::acquire_color(int2 size) +{ + return acquire(size, GPU_RGBA16F); +} + +GPUTexture *TexturePool::acquire_vector(int2 size) +{ + /* Vectors are stored in RGBA textures because RGB textures have limited support. */ + return acquire(size, GPU_RGBA16F); +} + +GPUTexture *TexturePool::acquire_float(int2 size) +{ + return acquire(size, GPU_R16F); +} + +void TexturePool::release(GPUTexture *texture) +{ + textures_.lookup(TexturePoolKey(texture)).append(texture); +} + +void TexturePool::reset() +{ + textures_.clear(); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/compositor/realtime_compositor/intern/utilities.cc b/source/blender/compositor/realtime_compositor/intern/utilities.cc new file mode 100644 index 00000000000..169ba70e9eb --- /dev/null +++ b/source/blender/compositor/realtime_compositor/intern/utilities.cc @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_assert.h" +#include "BLI_function_ref.hh" +#include "BLI_math_vec_types.hh" +#include "BLI_math_vector.hh" +#include "BLI_utildefines.h" + +#include "DNA_node_types.h" + +#include "NOD_derived_node_tree.hh" +#include "NOD_node_declaration.hh" + +#include "GPU_compute.h" +#include "GPU_shader.h" + +#include "COM_operation.hh" +#include "COM_result.hh" +#include "COM_utilities.hh" + +namespace blender::realtime_compositor { + +using namespace nodes::derived_node_tree_types; +using TargetSocketPathInfo = DOutputSocket::TargetSocketPathInfo; + +DSocket get_input_origin_socket(DInputSocket input) +{ + /* The input is unlinked. Return the socket itself. */ + if (input->logically_linked_sockets().is_empty()) { + return input; + } + + /* Only a single origin socket is guaranteed to exist. */ + DSocket socket; + input.foreach_origin_socket([&](const DSocket origin) { socket = origin; }); + return socket; +} + +DOutputSocket get_output_linked_to_input(DInputSocket input) +{ + /* Get the origin socket of this input, which will be an output socket if the input is linked + * to an output. */ + const DSocket origin = get_input_origin_socket(input); + + /* If the origin socket is an input, that means the input is unlinked, so return a null output + * socket. */ + if (origin->is_input()) { + return DOutputSocket(); + } + + /* Now that we know the origin is an output, return a derived output from it. */ + return DOutputSocket(origin); +} + +ResultType get_node_socket_result_type(const SocketRef *socket) +{ + switch (socket->bsocket()->type) { + case SOCK_FLOAT: + return ResultType::Float; + case SOCK_VECTOR: + return ResultType::Vector; + case SOCK_RGBA: + return ResultType::Color; + default: + BLI_assert_unreachable(); + return ResultType::Float; + } +} + +bool is_output_linked_to_node_conditioned(DOutputSocket output, FunctionRef<bool(DNode)> condition) +{ + bool condition_satisfied = false; + output.foreach_target_socket( + [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) { + if (condition(target.node())) { + condition_satisfied = true; + return; + } + }); + return condition_satisfied; +} + +int number_of_inputs_linked_to_output_conditioned(DOutputSocket output, + FunctionRef<bool(DInputSocket)> condition) +{ + int count = 0; + output.foreach_target_socket( + [&](DInputSocket target, const TargetSocketPathInfo &UNUSED(path_info)) { + if (condition(target)) { + count++; + } + }); + return count; +} + +bool is_shader_node(DNode node) +{ + return node->typeinfo()->get_compositor_shader_node; +} + +bool is_node_supported(DNode node) +{ + return node->typeinfo()->get_compositor_operation || + node->typeinfo()->get_compositor_shader_node; +} + +InputDescriptor input_descriptor_from_input_socket(const InputSocketRef *socket) +{ + using namespace nodes; + InputDescriptor input_descriptor; + input_descriptor.type = get_node_socket_result_type(socket); + const NodeDeclaration *node_declaration = socket->node().declaration(); + /* Not every node have a declaration, in which case, we assume the default values for the rest of + * the properties. */ + if (!node_declaration) { + return input_descriptor; + } + const SocketDeclarationPtr &socket_declaration = node_declaration->inputs()[socket->index()]; + input_descriptor.domain_priority = socket_declaration->compositor_domain_priority(); + input_descriptor.expects_single_value = socket_declaration->compositor_expects_single_value(); + return input_descriptor; +} + +void compute_dispatch_threads_at_least(GPUShader *shader, int2 threads_range, int2 local_size) +{ + /* If the threads range is divisible by the local size, dispatch the number of needed groups, + * which is their division. If it is not divisible, then dispatch an extra group to cover the + * remaining invocations, which means the actual threads range of the dispatch will be a bit + * larger than the given one. */ + const int2 groups_to_dispatch = math::divide_ceil(threads_range, local_size); + GPU_compute_dispatch(shader, groups_to_dispatch.x, groups_to_dispatch.y, 1); +} + +} // namespace blender::realtime_compositor diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index 00efa779c4d..a8b21e4c153 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -125,13 +125,13 @@ void DEG_tag_on_visible_update(struct Main *bmain, bool do_time); const char *DEG_update_tag_as_string(IDRecalcFlag flag); /** Tag given ID for an update in all the dependency graphs. */ -void DEG_id_tag_update(struct ID *id, int flag); -void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, int flag); +void DEG_id_tag_update(struct ID *id, unsigned int flags); +void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, unsigned int flags); void DEG_graph_id_tag_update(struct Main *bmain, struct Depsgraph *depsgraph, struct ID *id, - int flag); + unsigned int flags); /** Tag all dependency graphs when time has changed. */ void DEG_time_tag_update(struct Main *bmain); diff --git a/source/blender/depsgraph/DEG_depsgraph_build.h b/source/blender/depsgraph/DEG_depsgraph_build.h index 763d2d29035..ac6ab5c7666 100644 --- a/source/blender/depsgraph/DEG_depsgraph_build.h +++ b/source/blender/depsgraph/DEG_depsgraph_build.h @@ -161,8 +161,8 @@ void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle, * This function will take care of checking which operation is required to * have transformation for the modifier, taking into account possible simulation solvers. */ -void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle, - const char *description); +void DEG_add_depends_on_transform_relation(struct DepsNodeHandle *node_handle, + const char *description); /** * Adds relations from the given component of a given object to the given node diff --git a/source/blender/depsgraph/intern/builder/deg_builder.cc b/source/blender/depsgraph/intern/builder/deg_builder.cc index 5353f71685c..097c377ece4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder.cc @@ -13,6 +13,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_layer_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BLI_stack.h" @@ -95,6 +96,24 @@ bool DepsgraphBuilder::is_object_visibility_animated(const Object *object) return cache_->isPropertyAnimated(&object->id, property_id); } +bool DepsgraphBuilder::is_modifier_visibility_animated(const Object *object, + const ModifierData *modifier) +{ + AnimatedPropertyID property_id; + if (graph_->mode == DAG_EVAL_VIEWPORT) { + property_id = AnimatedPropertyID( + &object->id, &RNA_Modifier, (void *)modifier, "show_viewport"); + } + else if (graph_->mode == DAG_EVAL_RENDER) { + property_id = AnimatedPropertyID(&object->id, &RNA_Modifier, (void *)modifier, "show_render"); + } + else { + BLI_assert_msg(0, "Unknown evaluation mode."); + return false; + } + return cache_->isPropertyAnimated(&object->id, property_id); +} + bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan) { BLI_assert(object->type == OB_ARMATURE); diff --git a/source/blender/depsgraph/intern/builder/deg_builder.h b/source/blender/depsgraph/intern/builder/deg_builder.h index c44e5fd5f4d..5d043f1fd3a 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder.h +++ b/source/blender/depsgraph/intern/builder/deg_builder.h @@ -10,6 +10,7 @@ struct Base; struct ID; struct Main; +struct ModifierData; struct Object; struct bPoseChannel; @@ -25,6 +26,7 @@ class DepsgraphBuilder { virtual bool need_pull_base_into_graph(const Base *base); virtual bool is_object_visibility_animated(const Object *object); + virtual bool is_modifier_visibility_animated(const Object *object, const ModifierData *modifier); virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan); virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index be087c0b2d4..dd62a6cdea2 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -769,11 +769,7 @@ void DepsgraphNodeBuilder::build_object(int base_index, build_object(-1, object->parent, DEG_ID_LINKED_INDIRECTLY, is_visible); } /* Modifiers. */ - if (object->modifiers.first != nullptr) { - BuilderWalkUserData data; - data.builder = this; - BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); - } + build_object_modifiers(object); /* Grease Pencil Modifiers. */ if (object->greasepencil_modifiers.first != nullptr) { BuilderWalkUserData data; @@ -877,6 +873,44 @@ void DepsgraphNodeBuilder::build_object_instance_collection(Object *object, bool is_parent_collection_visible_ = is_current_parent_collection_visible; } +void DepsgraphNodeBuilder::build_object_modifiers(Object *object) +{ + if (BLI_listbase_is_empty(&object->modifiers)) { + return; + } + + const ModifierMode modifier_mode = (graph_->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime : + eModifierMode_Render; + + IDNode *id_node = find_id_node(&object->id); + + add_operation_node(&object->id, + NodeType::GEOMETRY, + OperationCode::VISIBILITY, + [id_node](::Depsgraph *depsgraph) { + deg_evaluate_object_modifiers_mode_node_visibility(depsgraph, id_node); + }); + + LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) { + OperationNode *modifier_node = add_operation_node( + &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, nullptr, modifier->name); + + /* Mute modifier mode if the modifier is not enabled for the dependency graph mode. + * This handles static (non-animated) mode of the modifier. */ + if ((modifier->mode & modifier_mode) == 0) { + modifier_node->flag |= DEPSOP_FLAG_MUTE; + } + + if (is_modifier_visibility_animated(object, modifier)) { + graph_->has_animated_visibility = true; + } + } + + BuilderWalkUserData data; + data.builder = this; + BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); +} + void DepsgraphNodeBuilder::build_object_data(Object *object) { if (object->data == nullptr) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 18e28311132..d5ac601ebff 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -174,6 +174,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_object_flags(int base_index, Object *object, eDepsNode_LinkedState_Type linked_state); + virtual void build_object_modifiers(Object *object); virtual void build_object_data(Object *object); virtual void build_object_data_camera(Object *object); virtual void build_object_data_geometry(Object *object); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index f36d94c7563..d6ee1286fc4 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -239,13 +239,8 @@ DepsgraphRelationBuilder::DepsgraphRelationBuilder(Main *bmain, { } -TimeSourceNode *DepsgraphRelationBuilder::get_node(const TimeSourceKey &key) const +TimeSourceNode *DepsgraphRelationBuilder::get_node(const TimeSourceKey & /*key*/) const { - if (key.id) { - /* XXX TODO */ - return nullptr; - } - return graph_->time_source; } @@ -298,12 +293,13 @@ bool DepsgraphRelationBuilder::has_node(const OperationKey &key) const return find_node(key) != nullptr; } -void DepsgraphRelationBuilder::add_modifier_to_transform_relation(const DepsNodeHandle *handle, - const char *description) +void DepsgraphRelationBuilder::add_depends_on_transform_relation(const DepsNodeHandle *handle, + const char *description) { IDNode *id_node = handle->node->owner->owner; ID *id = id_node->id_orig; - ComponentKey geometry_key(id, NodeType::GEOMETRY); + const OperationKey geometry_key( + id, NodeType::GEOMETRY, OperationCode::MODIFIER, handle->node->name.c_str()); /* Wire up the actual relation. */ add_depends_on_transform_relation(id, geometry_key, description); } @@ -723,11 +719,7 @@ void DepsgraphRelationBuilder::build_object(Object *object) } /* Modifiers. */ - if (object->modifiers.first != nullptr) { - BuilderWalkUserData data; - data.builder = this; - BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); - } + build_object_modifiers(object); /* Grease Pencil Modifiers. */ if (object->greasepencil_modifiers.first != nullptr) { @@ -875,6 +867,63 @@ void DepsgraphRelationBuilder::build_object_layer_component_relations(Object *ob add_relation(object_from_layer_exit_key, synchronize_key, "Synchronize to Original"); } +void DepsgraphRelationBuilder::build_object_modifiers(Object *object) +{ + if (BLI_listbase_is_empty(&object->modifiers)) { + return; + } + + const OperationKey eval_init_key( + &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL_INIT); + const OperationKey eval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); + + const ComponentKey object_visibility_key(&object->id, NodeType::VISIBILITY); + const OperationKey modifier_visibility_key( + &object->id, NodeType::GEOMETRY, OperationCode::VISIBILITY); + add_relation(modifier_visibility_key, + object_visibility_key, + "modifier -> object visibility", + RELATION_NO_VISIBILITY_CHANGE); + + add_relation(modifier_visibility_key, eval_key, "modifier visibility -> geometry eval"); + + ModifierUpdateDepsgraphContext ctx = {}; + ctx.scene = scene_; + ctx.object = object; + + OperationKey previous_key = eval_init_key; + LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) { + const OperationKey modifier_key( + &object->id, NodeType::GEOMETRY, OperationCode::MODIFIER, modifier->name); + + /* Relation for the modifier stack chain. */ + add_relation(previous_key, modifier_key, "Modifier"); + + const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)modifier->type); + if (mti->updateDepsgraph) { + const BuilderStack::ScopedEntry stack_entry = stack_.trace(*modifier); + + DepsNodeHandle handle = create_node_handle(modifier_key); + ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); + mti->updateDepsgraph(modifier, &ctx); + } + + /* Time dependency. */ + if (BKE_modifier_depends_ontime(scene_, modifier)) { + const TimeSourceKey time_src_key; + add_relation(time_src_key, modifier_key, "Time Source -> Modifier"); + } + + previous_key = modifier_key; + } + add_relation(previous_key, eval_key, "modifier stack order"); + + /* Build IDs referenced by the modifiers. */ + BuilderWalkUserData data; + data.builder = this; + BKE_modifiers_foreach_ID_link(object, modifier_walk, &data); +} + void DepsgraphRelationBuilder::build_object_data(Object *object) { if (object->data == nullptr) { @@ -1977,7 +2026,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) void DepsgraphRelationBuilder::build_particle_systems(Object *object) { - TimeSourceKey time_src_key; OperationKey obdata_ubereval_key(&object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); OperationKey eval_init_key( &object->id, NodeType::PARTICLE_SYSTEM, OperationCode::PARTICLE_SYSTEM_INIT); @@ -2199,26 +2247,6 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) * evaluated prior to Scene's CoW is ready. */ OperationKey scene_key(&scene_->id, NodeType::PARAMETERS, OperationCode::SCENE_EVAL); add_relation(scene_key, obdata_ubereval_key, "CoW Relation", RELATION_FLAG_NO_FLUSH); - /* Modifiers */ - if (object->modifiers.first != nullptr) { - ModifierUpdateDepsgraphContext ctx = {}; - ctx.scene = scene_; - ctx.object = object; - LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { - const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); - if (mti->updateDepsgraph) { - const BuilderStack::ScopedEntry stack_entry = stack_.trace(*md); - - DepsNodeHandle handle = create_node_handle(obdata_ubereval_key); - ctx.node = reinterpret_cast<::DepsNodeHandle *>(&handle); - mti->updateDepsgraph(md, &ctx); - } - if (BKE_modifier_depends_ontime(scene_, md)) { - TimeSourceKey time_src_key; - add_relation(time_src_key, obdata_ubereval_key, "Time Source -> Modifier"); - } - } - } /* Grease Pencil Modifiers. */ if (object->greasepencil_modifiers.first != nullptr) { ModifierUpdateDepsgraphContext ctx = {}; @@ -2262,9 +2290,9 @@ void DepsgraphRelationBuilder::build_object_data_geometry(Object *object) if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_LATTICE)) { // add geometry collider relations } - /* Make sure uber update is the last in the dependencies. */ - if (object->type != OB_ARMATURE) { - /* Armatures does no longer require uber node. */ + /* Make sure uber update is the last in the dependencies. + * Only do it here unless there are modifiers. This avoids transitive relations. */ + if (BLI_listbase_is_empty(&object->modifiers)) { OperationKey obdata_ubereval_key( &object->id, NodeType::GEOMETRY, OperationCode::GEOMETRY_EVAL); add_relation(geom_init_key, obdata_ubereval_key, "Object Geometry UberEval"); @@ -3097,7 +3125,6 @@ void DepsgraphRelationBuilder::build_copy_on_write_relations(IDNode *id_node) return; } - TimeSourceKey time_source_key; OperationKey copy_on_write_key(id_orig, NodeType::COPY_ON_WRITE, OperationCode::COPY_ON_WRITE); /* XXX: This is a quick hack to make Alt-A to work. */ // add_relation(time_source_key, copy_on_write_key, "Fluxgate capacitor hack"); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 7a78280f1f0..7d3a0fd9217 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -85,51 +85,113 @@ struct RootPChanMap; struct TimeSourceNode; struct TimeSourceKey { - TimeSourceKey(); - TimeSourceKey(ID *id); + TimeSourceKey() = default; string identifier() const; - - ID *id; }; struct ComponentKey { - ComponentKey(); - ComponentKey(ID *id, NodeType type, const char *name = ""); + ComponentKey() = default; + + inline ComponentKey(const ID *id, NodeType type, const char *name = "") + : id(id), type(type), name(name) + { + } string identifier() const; - ID *id; - NodeType type; - const char *name; + const ID *id = nullptr; + NodeType type = NodeType::UNDEFINED; + const char *name = ""; }; struct OperationKey { - OperationKey(); - OperationKey(ID *id, NodeType component_type, const char *name, int name_tag = -1); - OperationKey( - ID *id, NodeType component_type, const char *component_name, const char *name, int name_tag); + OperationKey() = default; + + inline OperationKey(const ID *id, NodeType component_type, const char *name, int name_tag = -1) + : id(id), + component_type(component_type), + component_name(""), + opcode(OperationCode::OPERATION), + name(name), + name_tag(name_tag) + { + } + + OperationKey(const ID *id, + NodeType component_type, + const char *component_name, + const char *name, + int name_tag) + : id(id), + component_type(component_type), + component_name(component_name), + opcode(OperationCode::OPERATION), + name(name), + name_tag(name_tag) + { + } + + OperationKey(const ID *id, NodeType component_type, OperationCode opcode) + : id(id), + component_type(component_type), + component_name(""), + opcode(opcode), + name(""), + name_tag(-1) + { + } - OperationKey(ID *id, NodeType component_type, OperationCode opcode); - OperationKey(ID *id, NodeType component_type, const char *component_name, OperationCode opcode); + OperationKey(const ID *id, + NodeType component_type, + const char *component_name, + OperationCode opcode) + : id(id), + component_type(component_type), + component_name(component_name), + opcode(opcode), + name(""), + name_tag(-1) + { + } - OperationKey( - ID *id, NodeType component_type, OperationCode opcode, const char *name, int name_tag = -1); - OperationKey(ID *id, + OperationKey(const ID *id, + NodeType component_type, + OperationCode opcode, + const char *name, + int name_tag = -1) + : id(id), + component_type(component_type), + component_name(""), + opcode(opcode), + name(name), + name_tag(name_tag) + { + } + + OperationKey(const ID *id, NodeType component_type, const char *component_name, OperationCode opcode, const char *name, - int name_tag = -1); + int name_tag = -1) + : id(id), + component_type(component_type), + component_name(component_name), + opcode(opcode), + name(name), + name_tag(name_tag) + { + } string identifier() const; - ID *id; - NodeType component_type; - const char *component_name; - OperationCode opcode; - const char *name; - int name_tag; + const ID *id = nullptr; + NodeType component_type = NodeType::UNDEFINED; + const char *component_name = ""; + OperationCode opcode = OperationCode::OPERATION; + const char *name = ""; + int name_tag = -1; }; struct RNAPathKey { @@ -177,7 +239,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { /* Adds relation from proper transformation operation to the modifier. * Takes care of checking for possible physics solvers modifying position * of this object. */ - void add_modifier_to_transform_relation(const DepsNodeHandle *handle, const char *description); + void add_depends_on_transform_relation(const DepsNodeHandle *handle, const char *description); void add_customdata_mask(Object *object, const DEGCustomDataMeshMasks &customdata_masks); void add_special_eval_flag(ID *id, uint32_t flag); @@ -203,6 +265,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_object(Object *object); virtual void build_object_from_view_layer_base(Object *object); virtual void build_object_layer_component_relations(Object *object); + virtual void build_object_modifiers(Object *object); virtual void build_object_data(Object *object); virtual void build_object_data_camera(Object *object); virtual void build_object_data_geometry(Object *object); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc index eeaab623482..8506a97c408 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations_keys.cc @@ -14,14 +14,6 @@ namespace blender::deg { //////////////////////////////////////////////////////////////////////////////// /* Time source. */ -TimeSourceKey::TimeSourceKey() : id(nullptr) -{ -} - -TimeSourceKey::TimeSourceKey(ID *id) : id(id) -{ -} - string TimeSourceKey::identifier() const { return string("TimeSourceKey"); @@ -30,15 +22,6 @@ string TimeSourceKey::identifier() const //////////////////////////////////////////////////////////////////////////////// // Component. -ComponentKey::ComponentKey() : id(nullptr), type(NodeType::UNDEFINED), name("") -{ -} - -ComponentKey::ComponentKey(ID *id, NodeType type, const char *name) - : id(id), type(type), name(name) -{ -} - string ComponentKey::identifier() const { const char *idname = (id) ? id->name : "<None>"; @@ -55,86 +38,6 @@ string ComponentKey::identifier() const //////////////////////////////////////////////////////////////////////////////// // Operation. -OperationKey::OperationKey() - : id(nullptr), - component_type(NodeType::UNDEFINED), - component_name(""), - opcode(OperationCode::OPERATION), - name(""), - name_tag(-1) -{ -} - -OperationKey::OperationKey(ID *id, NodeType component_type, const char *name, int name_tag) - : id(id), - component_type(component_type), - component_name(""), - opcode(OperationCode::OPERATION), - name(name), - name_tag(name_tag) -{ -} - -OperationKey::OperationKey( - ID *id, NodeType component_type, const char *component_name, const char *name, int name_tag) - : id(id), - component_type(component_type), - component_name(component_name), - opcode(OperationCode::OPERATION), - name(name), - name_tag(name_tag) -{ -} - -OperationKey::OperationKey(ID *id, NodeType component_type, OperationCode opcode) - : id(id), - component_type(component_type), - component_name(""), - opcode(opcode), - name(""), - name_tag(-1) -{ -} - -OperationKey::OperationKey(ID *id, - NodeType component_type, - const char *component_name, - OperationCode opcode) - : id(id), - component_type(component_type), - component_name(component_name), - opcode(opcode), - name(""), - name_tag(-1) -{ -} - -OperationKey::OperationKey( - ID *id, NodeType component_type, OperationCode opcode, const char *name, int name_tag) - : id(id), - component_type(component_type), - component_name(""), - opcode(opcode), - name(name), - name_tag(name_tag) -{ -} - -OperationKey::OperationKey(ID *id, - NodeType component_type, - const char *component_name, - OperationCode opcode, - const char *name, - int name_tag) - : id(id), - component_type(component_type), - component_name(component_name), - opcode(opcode), - name(name), - name_tag(name_tag) -{ -} - string OperationKey::identifier() const { string result = string("OperationKey("); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc index 5202ada5408..d94746fb7fa 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_rna.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_rna.cc @@ -225,6 +225,10 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, } return node_identifier; } + + const char *prop_identifier = prop != nullptr ? RNA_property_identifier((PropertyRNA *)prop) : + ""; + if (RNA_struct_is_a(ptr->type, &RNA_Constraint)) { const Object *object = reinterpret_cast<const Object *>(ptr->owner_id); const bConstraint *constraint = static_cast<const bConstraint *>(ptr->data); @@ -264,6 +268,13 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, return node_identifier; } } + else if (RNA_struct_is_a(ptr->type, &RNA_Modifier) && + (contains(prop_identifier, "show_viewport") || + contains(prop_identifier, "show_render"))) { + node_identifier.type = NodeType::GEOMETRY; + node_identifier.operation_code = OperationCode::VISIBILITY; + return node_identifier; + } else if (RNA_struct_is_a(ptr->type, &RNA_Mesh) || RNA_struct_is_a(ptr->type, &RNA_Modifier) || RNA_struct_is_a(ptr->type, &RNA_GpencilModifier) || RNA_struct_is_a(ptr->type, &RNA_Spline) || RNA_struct_is_a(ptr->type, &RNA_TextBox) || @@ -290,7 +301,6 @@ RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr, else if (ptr->type == &RNA_Object) { /* Transforms props? */ if (prop != nullptr) { - const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop); /* TODO(sergey): How to optimize this? */ if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") || contains(prop_identifier, "matrix_channel") || diff --git a/source/blender/depsgraph/intern/depsgraph_build.cc b/source/blender/depsgraph/intern/depsgraph_build.cc index a207c13d646..6da290d6c4e 100644 --- a/source/blender/depsgraph/intern/depsgraph_build.cc +++ b/source/blender/depsgraph/intern/depsgraph_build.cc @@ -199,11 +199,11 @@ void DEG_add_generic_id_relation(struct DepsNodeHandle *node_handle, deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description); } -void DEG_add_modifier_to_transform_relation(struct DepsNodeHandle *node_handle, - const char *description) +void DEG_add_depends_on_transform_relation(struct DepsNodeHandle *node_handle, + const char *description) { deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle); - deg_node_handle->builder->add_modifier_to_transform_relation(deg_node_handle, description); + deg_node_handle->builder->add_depends_on_transform_relation(deg_node_handle, description); } void DEG_add_special_eval_flag(struct DepsNodeHandle *node_handle, ID *id, uint32_t flag) diff --git a/source/blender/depsgraph/intern/depsgraph_relation.h b/source/blender/depsgraph/intern/depsgraph_relation.h index 1bacb9abfa6..3f316fa84e8 100644 --- a/source/blender/depsgraph/intern/depsgraph_relation.h +++ b/source/blender/depsgraph/intern/depsgraph_relation.h @@ -28,6 +28,8 @@ enum RelationFlag { RELATION_FLAG_GODMODE = (1 << 4), /* Relation will check existence before being added. */ RELATION_CHECK_BEFORE_ADD = (1 << 5), + /* The relation does not participate in visibility checks. */ + RELATION_NO_VISIBILITY_CHANGE = (1 << 6), }; /* B depends on A (A -> B) */ diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 9cd5980d8fe..cc742b98866 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -220,17 +220,28 @@ void depsgraph_tag_to_component_opcode(const ID *id, *component_type = NodeType::NTREE_OUTPUT; *operation_code = OperationCode::NTREE_OUTPUT; break; + + case ID_RECALC_PROVISION_26: + case ID_RECALC_PROVISION_27: + case ID_RECALC_PROVISION_28: + case ID_RECALC_PROVISION_29: + case ID_RECALC_PROVISION_30: + case ID_RECALC_PROVISION_31: + /* Silently ignore. + * The bits might be passed here from ID_RECALC_ALL. This is not a code-mistake, but just the + * way how the recalc flags are handled. */ + break; } } void id_tag_update_ntree_special( - Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source) + Main *bmain, Depsgraph *graph, ID *id, unsigned int flags, eUpdateSource update_source) { bNodeTree *ntree = ntreeFromID(id); if (ntree == nullptr) { return; } - graph_id_tag_update(bmain, graph, &ntree->id, flag, update_source); + graph_id_tag_update(bmain, graph, &ntree->id, flags, update_source); } void depsgraph_update_editors_tag(Main *bmain, Depsgraph *graph, ID *id) @@ -407,13 +418,13 @@ string stringify_append_bit(const string &str, IDRecalcFlag tag) return result; } -string stringify_update_bitfield(int flag) +string stringify_update_bitfield(unsigned int flags) { - if (flag == 0) { + if (flags == 0) { return "LEGACY_0"; } string result; - int current_flag = flag; + unsigned int current_flag = flags; /* Special cases to avoid ALL flags form being split into * individual bits. */ if ((current_flag & ID_RECALC_PSYS_ALL) == ID_RECALC_PSYS_ALL) { @@ -421,7 +432,7 @@ string stringify_update_bitfield(int flag) } /* Handle all the rest of the flags. */ while (current_flag != 0) { - IDRecalcFlag tag = (IDRecalcFlag)(1 << bitscan_forward_clear_i(¤t_flag)); + IDRecalcFlag tag = (IDRecalcFlag)(1 << bitscan_forward_clear_uint(¤t_flag)); result = stringify_append_bit(result, tag); } return result; @@ -449,7 +460,7 @@ int deg_recalc_flags_for_legacy_zero() ID_RECALC_SOURCE | ID_RECALC_EDITORS); } -int deg_recalc_flags_effective(Depsgraph *graph, int flags) +int deg_recalc_flags_effective(Depsgraph *graph, unsigned int flags) { if (graph != nullptr) { if (!graph->is_active) { @@ -520,12 +531,12 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph) * No need bother with it to tag or anything. */ continue; } - int flag = 0; + unsigned int flags = 0; if (!deg::deg_copy_on_write_is_expanded(id_node->id_cow)) { - flag |= ID_RECALC_COPY_ON_WRITE; + flags |= ID_RECALC_COPY_ON_WRITE; if (do_time) { if (BKE_animdata_from_id(id_node->id_orig) != nullptr) { - flag |= ID_RECALC_ANIMATION; + flags |= ID_RECALC_ANIMATION; } } } @@ -542,9 +553,9 @@ void graph_tag_ids_for_visible_update(Depsgraph *graph) * * TODO(sergey): Need to generalize this somehow. */ if (id_type == ID_OB) { - flag |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY; + flags |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY; } - graph_id_tag_update(bmain, graph, id_node->id_orig, flag, DEG_UPDATE_SOURCE_VISIBILITY); + graph_id_tag_update(bmain, graph, id_node->id_orig, flags, DEG_UPDATE_SOURCE_VISIBILITY); if (id_type == ID_SCE) { /* Make sure collection properties are up to date. */ id_node->tag_update(graph, DEG_UPDATE_SOURCE_VISIBILITY); @@ -614,20 +625,20 @@ NodeType geometry_tag_to_component(const ID *id) return NodeType::UNDEFINED; } -void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source) +void id_tag_update(Main *bmain, ID *id, unsigned int flags, eUpdateSource update_source) { - graph_id_tag_update(bmain, nullptr, id, flag, update_source); + graph_id_tag_update(bmain, nullptr, id, flags, update_source); for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) { - graph_id_tag_update(bmain, depsgraph, id, flag, update_source); + graph_id_tag_update(bmain, depsgraph, id, flags, update_source); } /* Accumulate all tags for an ID between two undo steps, so they can be * replayed for undo. */ - id->recalc_after_undo_push |= deg_recalc_flags_effective(nullptr, flag); + id->recalc_after_undo_push |= deg_recalc_flags_effective(nullptr, flags); } void graph_id_tag_update( - Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source) + Main *bmain, Depsgraph *graph, ID *id, unsigned int flags, eUpdateSource update_source) { const int debug_flags = (graph != nullptr) ? DEG_debug_flags_get((::Depsgraph *)graph) : G.debug; if (graph != nullptr && graph->is_evaluating) { @@ -640,20 +651,20 @@ void graph_id_tag_update( printf("%s: id=%s flags=%s source=%s\n", __func__, id->name, - stringify_update_bitfield(flag).c_str(), + stringify_update_bitfield(flags).c_str(), update_source_as_string(update_source)); } IDNode *id_node = (graph != nullptr) ? graph->find_id_node(id) : nullptr; if (graph != nullptr) { DEG_graph_id_type_tag(reinterpret_cast<::Depsgraph *>(graph), GS(id->name)); } - if (flag == 0) { + if (flags == 0) { deg_graph_node_tag_zero(bmain, graph, id_node, update_source); } /* Store original flag in the ID. * Allows to have more granularity than a node-factory based flags. */ if (id_node != nullptr) { - id_node->id_cow->recalc |= flag; + id_node->id_cow->recalc |= flags; } /* When ID is tagged for update based on an user edits store the recalc flags in the original ID. * This way IDs in the undo steps will have this flag preserved, making it possible to restore @@ -663,20 +674,20 @@ void graph_id_tag_update( * usually newly created dependency graph skips animation update to avoid loss of unkeyed * changes). */ if (update_source == DEG_UPDATE_SOURCE_USER_EDIT) { - id->recalc |= deg_recalc_flags_effective(graph, flag); + id->recalc |= deg_recalc_flags_effective(graph, flags); } - int current_flag = flag; + unsigned int current_flag = flags; while (current_flag != 0) { - IDRecalcFlag tag = (IDRecalcFlag)(1 << bitscan_forward_clear_i(¤t_flag)); + IDRecalcFlag tag = (IDRecalcFlag)(1 << bitscan_forward_clear_uint(¤t_flag)); graph_id_tag_update_single_flag(bmain, graph, id, id_node, tag, update_source); } /* Special case for nested node tree data-blocks. */ - id_tag_update_ntree_special(bmain, graph, id, flag, update_source); + id_tag_update_ntree_special(bmain, graph, id, flags, update_source); /* Direct update tags means that something outside of simulated/cached * physics did change and that cache is to be invalidated. * This is only needed if data changes. If it's just a drawing, we keep the * point cache. */ - if (update_source == DEG_UPDATE_SOURCE_USER_EDIT && flag != ID_RECALC_SHADING) { + if (update_source == DEG_UPDATE_SOURCE_USER_EDIT && flags != ID_RECALC_SHADING) { graph_id_tag_update_single_flag( bmain, graph, id, id_node, ID_RECALC_POINT_CACHE, update_source); } @@ -741,33 +752,45 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "TAG_FOR_UNDO"; case ID_RECALC_NTREE_OUTPUT: return "ID_RECALC_NTREE_OUTPUT"; + + case ID_RECALC_PROVISION_26: + case ID_RECALC_PROVISION_27: + case ID_RECALC_PROVISION_28: + case ID_RECALC_PROVISION_29: + case ID_RECALC_PROVISION_30: + case ID_RECALC_PROVISION_31: + /* Silently return nullptr, indicating that there is no string representation. + * + * This is needed due to the way how logging for ID_RECALC_ALL works: it iterates over all + * bits and converts then to string. */ + return nullptr; } return nullptr; } /* Data-Based Tagging. */ -void DEG_id_tag_update(ID *id, int flag) +void DEG_id_tag_update(ID *id, unsigned int flags) { - DEG_id_tag_update_ex(G.main, id, flag); + DEG_id_tag_update_ex(G.main, id, flags); } -void DEG_id_tag_update_ex(Main *bmain, ID *id, int flag) +void DEG_id_tag_update_ex(Main *bmain, ID *id, unsigned int flags) { if (id == nullptr) { /* Ideally should not happen, but old depsgraph allowed this. */ return; } - deg::id_tag_update(bmain, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT); + deg::id_tag_update(bmain, id, flags, deg::DEG_UPDATE_SOURCE_USER_EDIT); } void DEG_graph_id_tag_update(struct Main *bmain, struct Depsgraph *depsgraph, struct ID *id, - int flag) + unsigned int flags) { deg::Depsgraph *graph = (deg::Depsgraph *)depsgraph; - deg::graph_id_tag_update(bmain, graph, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT); + deg::graph_id_tag_update(bmain, graph, id, flags, deg::DEG_UPDATE_SOURCE_USER_EDIT); } void DEG_time_tag_update(struct Main *bmain) diff --git a/source/blender/depsgraph/intern/depsgraph_tag.h b/source/blender/depsgraph/intern/depsgraph_tag.h index b722aab5719..61643e6f740 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.h +++ b/source/blender/depsgraph/intern/depsgraph_tag.h @@ -18,11 +18,11 @@ struct Depsgraph; NodeType geometry_tag_to_component(const ID *id); /* Tag given ID for an update in all registered dependency graphs. */ -void id_tag_update(Main *bmain, ID *id, int flag, eUpdateSource update_source); +void id_tag_update(Main *bmain, ID *id, unsigned int flags, eUpdateSource update_source); /* Tag given ID for an update with in a given dependency graph. */ void graph_id_tag_update( - Main *bmain, Depsgraph *graph, ID *id, int flag, eUpdateSource update_source); + Main *bmain, Depsgraph *graph, ID *id, unsigned int flags, eUpdateSource update_source); /* Tag IDs of the graph for the visibility update tags. * Will do nothing if the graph is not tagged for visibility update. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index 9b2ce2bb707..cd0015ff717 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -437,7 +437,7 @@ void deg_evaluate_on_refresh(Depsgraph *graph) evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_WRITE); - if (graph->has_animated_visibility) { + if (graph->has_animated_visibility || graph->need_update_nodes_visibility) { /* Update pending parents including only the ones which are affecting operations which are * affecting visibility. */ state.need_update_pending_parents = true; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc index 05f7631b0d4..a056ba1dfa7 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.cc @@ -8,6 +8,7 @@ #include "intern/eval/deg_eval_visibility.h" #include "DNA_layer_types.h" +#include "DNA_modifier_types.h" #include "DNA_object_types.h" #include "BLI_assert.h" @@ -47,6 +48,42 @@ void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node } } +void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node) +{ + BLI_assert(GS(id_node->id_cow->name) == ID_OB); + + Depsgraph *graph = reinterpret_cast<Depsgraph *>(depsgraph); + const Object *object = reinterpret_cast<const Object *>(id_node->id_cow); + + DEG_debug_print_eval(depsgraph, __func__, object->id.name, &object->id); + + if (BLI_listbase_is_empty(&object->modifiers)) { + return; + } + + const ModifierMode modifier_mode = (graph->mode == DAG_EVAL_VIEWPORT) ? eModifierMode_Realtime : + eModifierMode_Render; + + const ComponentNode *geometry_component = id_node->find_component(NodeType::GEOMETRY); + LISTBASE_FOREACH (ModifierData *, modifier, &object->modifiers) { + OperationNode *modifier_node = geometry_component->find_operation(OperationCode::MODIFIER, + modifier->name); + + BLI_assert_msg(modifier_node != nullptr, + "Modifier node in depsgraph is not found. Likely due to missing " + "DEG_relations_tag_update()."); + + const bool modifier_enabled = modifier->mode & modifier_mode; + const int mute_flag = modifier_enabled ? 0 : DEPSOP_FLAG_MUTE; + if ((modifier_node->flag & DEPSOP_FLAG_MUTE) != mute_flag) { + modifier_node->flag &= ~DEPSOP_FLAG_MUTE; + modifier_node->flag |= mute_flag; + + graph->need_update_nodes_visibility = true; + } + } +} + void deg_graph_flush_visibility_flags(Depsgraph *graph) { enum { @@ -116,10 +153,30 @@ void deg_graph_flush_visibility_flags(Depsgraph *graph) OperationNode *op_from = reinterpret_cast<OperationNode *>(rel->from); ComponentNode *comp_from = op_from->owner; + op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY); + + if (rel->flag & RELATION_NO_VISIBILITY_CHANGE) { + continue; + } + const bool target_possibly_affects_visible_id = comp_to->possibly_affects_visible_id; - const bool target_affects_visible_id = comp_to->affects_visible_id; - op_from->flag |= (op_to->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY); + bool target_affects_visible_id = comp_to->affects_visible_id; + + /* This is a bit arbitrary but the idea here is following: + * + * - When another object is used by a disabled modifier we do not want that object to + * be considered needed for evaluation. + * + * - However, we do not want to take mute flag during visibility propagation within the + * same object. Otherwise drivers and transform dependencies of the geometry component + * entry component might not be properly handled. + * + * This code works fine for muting modifiers, but might need tweaks when mute is used for + * something else. */ + if (comp_from != comp_to && (op_to->flag & DEPSOP_FLAG_MUTE)) { + target_affects_visible_id = false; + } /* Visibility component forces all components of the current ID to be considered as * affecting directly visible. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h index 9e9db8ab34a..6544654f3cf 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_visibility.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_visibility.h @@ -18,6 +18,9 @@ struct IDNode; * restriction flags. */ void deg_evaluate_object_node_visibility(::Depsgraph *depsgraph, IDNode *id_node); +/* Update node visibility flags based on actual modifiers mode flags. */ +void deg_evaluate_object_modifiers_mode_node_visibility(::Depsgraph *depsgraph, IDNode *id_node); + /* Flush both static and dynamic visibility flags from leaves up to the roots, making it possible * to know whether a node has affect on something (potentially) visible. */ void deg_graph_flush_visibility_flags(Depsgraph *graph); diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.cc b/source/blender/depsgraph/intern/node/deg_node_operation.cc index de4bc0668cf..016af735fcf 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.cc +++ b/source/blender/depsgraph/intern/node/deg_node_operation.cc @@ -84,6 +84,8 @@ const char *operationCodeAsString(OperationCode opcode) /* Geometry. */ case OperationCode::GEOMETRY_EVAL_INIT: return "GEOMETRY_EVAL_INIT"; + case OperationCode::MODIFIER: + return "MODIFIER"; case OperationCode::GEOMETRY_EVAL: return "GEOMETRY_EVAL"; case OperationCode::GEOMETRY_EVAL_DONE: @@ -225,6 +227,16 @@ void OperationNode::tag_update(Depsgraph *graph, eUpdateSource source) * the evaluated clues that evaluation needs to happen again. */ graph->add_entry_tag(this); + /* Enforce dynamic visibility code-path update. + * This ensures visibility flags are consistently propagated throughout the dependency graph when + * there is no animated visibility in the graph. + * + * For example this ensures that graph is updated properly when manually toggling non-animated + * modifier visibility. */ + if (opcode == OperationCode::VISIBILITY) { + graph->need_update_nodes_visibility = true; + } + /* Tag for update, but also note that this was the source of an update. */ flag |= (DEPSOP_FLAG_NEEDS_UPDATE | DEPSOP_FLAG_DIRECTLY_MODIFIED); switch (source) { diff --git a/source/blender/depsgraph/intern/node/deg_node_operation.h b/source/blender/depsgraph/intern/node/deg_node_operation.h index 656b27550f6..cb3beb56556 100644 --- a/source/blender/depsgraph/intern/node/deg_node_operation.h +++ b/source/blender/depsgraph/intern/node/deg_node_operation.h @@ -84,6 +84,8 @@ enum class OperationCode { /* Initialize evaluation of the geometry. Is an entry operation of geometry * component. */ GEOMETRY_EVAL_INIT, + /* Modifier. */ + MODIFIER, /* Evaluate the whole geometry, including modifiers. */ GEOMETRY_EVAL, /* Evaluation of geometry is completely done. */ @@ -217,6 +219,9 @@ enum OperationFlag { /* The operation directly or indirectly affects ID node visibility. */ DEPSOP_FLAG_AFFECTS_VISIBILITY = (1 << 4), + /* Evaluation of the node is temporarily disabled. */ + DEPSOP_FLAG_MUTE = (1 << 5), + /* Set of flags which gets flushed along the relations. */ DEPSOP_FLAG_FLUSH = (DEPSOP_FLAG_USER_MODIFIED), }; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 30352d3d19c..322b2e78caa 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -23,6 +23,7 @@ set(INC ../nodes ../render ../render/intern + ../compositor/realtime_compositor ../windowmanager ../../../intern/atomic @@ -77,13 +78,13 @@ set(SRC intern/draw_cache_impl_mesh.cc intern/draw_cache_impl_metaball.c intern/draw_cache_impl_particles.c - intern/draw_cache_impl_pointcloud.c + intern/draw_cache_impl_pointcloud.cc intern/draw_cache_impl_subdivision.cc intern/draw_cache_impl_volume.c intern/draw_color_management.cc intern/draw_common.c intern/draw_curves.cc - intern/draw_debug.c + intern/draw_debug.cc intern/draw_fluid.c intern/draw_hair.cc intern/draw_instance_data.c @@ -103,6 +104,7 @@ set(SRC intern/smaa_textures.c engines/basic/basic_engine.c engines/basic/basic_shader.c + engines/compositor/compositor_engine.cc engines/image/image_engine.cc engines/image/image_shader.cc engines/eevee/eevee_bloom.c @@ -134,10 +136,13 @@ set(SRC engines/eevee/eevee_temporal_sampling.c engines/eevee/eevee_volumes.c engines/eevee_next/eevee_camera.cc + engines/eevee_next/eevee_depth_of_field.cc engines/eevee_next/eevee_engine.cc engines/eevee_next/eevee_film.cc engines/eevee_next/eevee_instance.cc + engines/eevee_next/eevee_light.cc engines/eevee_next/eevee_material.cc + engines/eevee_next/eevee_motion_blur.cc engines/eevee_next/eevee_pipeline.cc engines/eevee_next/eevee_renderbuffers.cc engines/eevee_next/eevee_sampling.cc @@ -212,6 +217,7 @@ set(SRC intern/draw_common_shader_shared.h intern/draw_curves_private.h intern/draw_debug.h + intern/draw_debug.hh intern/draw_hair_private.h intern/draw_instance_data.h intern/draw_manager.h @@ -228,6 +234,7 @@ set(SRC intern/smaa_textures.h engines/basic/basic_engine.h engines/basic/basic_private.h + engines/compositor/compositor_engine.h engines/eevee/eevee_engine.h engines/eevee/eevee_lightcache.h engines/eevee/eevee_lut.h @@ -259,6 +266,7 @@ set(SRC set(LIB bf_blenkernel bf_blenlib + bf_realtime_compositor bf_windowmanager ) @@ -361,6 +369,22 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_attributes_lib.glsl engines/eevee_next/shaders/eevee_camera_lib.glsl + engines/eevee_next/shaders/eevee_colorspace_lib.glsl + engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl + engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl + engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl + engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl + engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl + engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl engines/eevee_next/shaders/eevee_film_comp.glsl engines/eevee_next/shaders/eevee_film_frag.glsl engines/eevee_next/shaders/eevee_film_lib.glsl @@ -368,7 +392,21 @@ set(GLSL_SRC engines/eevee_next/shaders/eevee_geom_gpencil_vert.glsl engines/eevee_next/shaders/eevee_geom_mesh_vert.glsl engines/eevee_next/shaders/eevee_geom_world_vert.glsl + engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl + engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl + engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl + engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl + engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl + engines/eevee_next/shaders/eevee_light_eval_lib.glsl + engines/eevee_next/shaders/eevee_light_iter_lib.glsl + engines/eevee_next/shaders/eevee_light_lib.glsl + engines/eevee_next/shaders/eevee_ltc_lib.glsl + engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl + engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl + engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl + engines/eevee_next/shaders/eevee_motion_blur_lib.glsl engines/eevee_next/shaders/eevee_nodetree_lib.glsl + engines/eevee_next/shaders/eevee_sampling_lib.glsl engines/eevee_next/shaders/eevee_surf_deferred_frag.glsl engines/eevee_next/shaders/eevee_surf_depth_frag.glsl engines/eevee_next/shaders/eevee_surf_forward_frag.glsl @@ -409,22 +447,25 @@ set(GLSL_SRC engines/workbench/workbench_shader_shared.h + intern/shaders/common_aabb_lib.glsl intern/shaders/common_attribute_lib.glsl intern/shaders/common_colormanagement_lib.glsl + intern/shaders/common_debug_draw_lib.glsl + intern/shaders/common_debug_print_lib.glsl + intern/shaders/common_debug_shape_lib.glsl + intern/shaders/common_fullscreen_vert.glsl + intern/shaders/common_fxaa_lib.glsl intern/shaders/common_globals_lib.glsl intern/shaders/common_gpencil_lib.glsl - intern/shaders/common_pointcloud_lib.glsl intern/shaders/common_hair_lib.glsl - intern/shaders/common_hair_refine_vert.glsl intern/shaders/common_hair_refine_comp.glsl - intern/shaders/common_math_lib.glsl + intern/shaders/common_hair_refine_vert.glsl + intern/shaders/common_intersect_lib.glsl intern/shaders/common_math_geom_lib.glsl - intern/shaders/common_view_clipping_lib.glsl - intern/shaders/common_view_lib.glsl - intern/shaders/common_fxaa_lib.glsl + intern/shaders/common_math_lib.glsl + intern/shaders/common_pointcloud_lib.glsl + intern/shaders/common_shape_lib.glsl intern/shaders/common_smaa_lib.glsl - intern/shaders/common_fullscreen_vert.glsl - intern/shaders/common_subdiv_custom_data_interp_comp.glsl intern/shaders/common_subdiv_ibo_lines_comp.glsl intern/shaders/common_subdiv_ibo_tris_comp.glsl @@ -437,6 +478,13 @@ set(GLSL_SRC intern/shaders/common_subdiv_vbo_edituv_strech_area_comp.glsl intern/shaders/common_subdiv_vbo_lnor_comp.glsl intern/shaders/common_subdiv_vbo_sculpt_data_comp.glsl + intern/shaders/common_view_clipping_lib.glsl + intern/shaders/common_view_lib.glsl + intern/shaders/draw_debug_draw_display_frag.glsl + intern/shaders/draw_debug_draw_display_vert.glsl + intern/shaders/draw_debug_info.hh + intern/shaders/draw_debug_print_display_frag.glsl + intern/shaders/draw_debug_print_display_vert.glsl intern/draw_common_shader_shared.h intern/draw_shader_shared.h diff --git a/source/blender/draw/engines/compositor/compositor_engine.cc b/source/blender/draw/engines/compositor/compositor_engine.cc new file mode 100644 index 00000000000..f36a59a4ce6 --- /dev/null +++ b/source/blender/draw/engines/compositor/compositor_engine.cc @@ -0,0 +1,203 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_listbase.h" +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "DNA_ID_enums.h" +#include "DNA_scene_types.h" + +#include "DEG_depsgraph_query.h" + +#include "DRW_render.h" + +#include "IMB_colormanagement.h" + +#include "COM_context.hh" +#include "COM_evaluator.hh" +#include "COM_texture_pool.hh" + +#include "GPU_texture.h" + +namespace blender::draw::compositor { + +class TexturePool : public realtime_compositor::TexturePool { + public: + GPUTexture *allocate_texture(int2 size, eGPUTextureFormat format) override + { + DrawEngineType *owner = (DrawEngineType *)this; + return DRW_texture_pool_query_2d(size.x, size.y, format, owner); + } +}; + +class Context : public realtime_compositor::Context { + private: + /* A pointer to the info message of the compositor engine. This is a char array of size + * GPU_INFO_SIZE. The message is cleared prior to updating or evaluating the compositor. */ + char *info_message_; + + public: + Context(realtime_compositor::TexturePool &texture_pool, char *info_message) + : realtime_compositor::Context(texture_pool), info_message_(info_message) + { + } + + const Scene *get_scene() const override + { + return DRW_context_state_get()->scene; + } + + int2 get_output_size() override + { + return int2(float2(DRW_viewport_size_get())); + } + + GPUTexture *get_output_texture() override + { + return DRW_viewport_texture_list_get()->color; + } + + GPUTexture *get_input_texture(int UNUSED(view_layer), eScenePassType UNUSED(pass_type)) override + { + return get_output_texture(); + } + + StringRef get_view_name() override + { + const SceneRenderView *view = static_cast<SceneRenderView *>( + BLI_findlink(&get_scene()->r.views, DRW_context_state_get()->v3d->multiview_eye)); + return view->name; + } + + void set_info_message(StringRef message) const override + { + message.copy(info_message_, GPU_INFO_SIZE); + } +}; + +class Engine { + private: + TexturePool texture_pool_; + Context context_; + realtime_compositor::Evaluator evaluator_; + /* Stores the viewport size at the time the last compositor evaluation happened. See the + * update_viewport_size method for more information. */ + int2 last_viewport_size_; + + public: + Engine(char *info_message) + : context_(texture_pool_, info_message), + evaluator_(context_, node_tree()), + last_viewport_size_(context_.get_output_size()) + { + } + + /* Update the viewport size and evaluate the compositor. */ + void draw() + { + update_viewport_size(); + evaluator_.evaluate(); + } + + /* If the size of the viewport changed from the last time the compositor was evaluated, update + * the viewport size and reset the evaluator. That's because the evaluator compiles the node tree + * in a manner that is specifically optimized for the size of the viewport. This should be called + * before evaluating the compositor. */ + void update_viewport_size() + { + if (last_viewport_size_ == context_.get_output_size()) { + return; + } + + last_viewport_size_ = context_.get_output_size(); + + evaluator_.reset(); + } + + /* If the compositor node tree changed, reset the evaluator. */ + void update(const Depsgraph *depsgraph) + { + if (DEG_id_type_updated(depsgraph, ID_NT)) { + evaluator_.reset(); + } + } + + /* Get a reference to the compositor node tree. */ + static bNodeTree &node_tree() + { + return *DRW_context_state_get()->scene->nodetree; + } +}; + +} // namespace blender::draw::compositor + +using namespace blender::draw::compositor; + +struct COMPOSITOR_Data { + DrawEngineType *engine_type; + DRWViewportEmptyList *fbl; + DRWViewportEmptyList *txl; + DRWViewportEmptyList *psl; + DRWViewportEmptyList *stl; + Engine *instance_data; + char info[GPU_INFO_SIZE]; +}; + +static void compositor_engine_init(void *data) +{ + COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data); + + if (!compositor_data->instance_data) { + compositor_data->instance_data = new Engine(compositor_data->info); + } +} + +static void compositor_engine_free(void *instance_data) +{ + Engine *engine = static_cast<Engine *>(instance_data); + delete engine; +} + +static void compositor_engine_draw(void *data) +{ + const COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data); + compositor_data->instance_data->draw(); +} + +static void compositor_engine_update(void *data) +{ + COMPOSITOR_Data *compositor_data = static_cast<COMPOSITOR_Data *>(data); + + /* Clear any info message that was set in a previous update. */ + compositor_data->info[0] = '\0'; + + if (compositor_data->instance_data) { + compositor_data->instance_data->update(DRW_context_state_get()->depsgraph); + } +} + +extern "C" { + +static const DrawEngineDataSize compositor_data_size = DRW_VIEWPORT_DATA_SIZE(COMPOSITOR_Data); + +DrawEngineType draw_engine_compositor_type = { + nullptr, /* next */ + nullptr, /* prev */ + N_("Compositor"), /* idname */ + &compositor_data_size, /* vedata_size */ + &compositor_engine_init, /* engine_init */ + nullptr, /* engine_free */ + &compositor_engine_free, /* instance_free */ + nullptr, /* cache_init */ + nullptr, /* cache_populate */ + nullptr, /* cache_finish */ + &compositor_engine_draw, /* draw_scene */ + &compositor_engine_update, /* view_update */ + nullptr, /* id_update */ + nullptr, /* render_to_image */ + nullptr, /* store_metadata */ +}; +} diff --git a/source/blender/draw/engines/compositor/compositor_engine.h b/source/blender/draw/engines/compositor/compositor_engine.h new file mode 100644 index 00000000000..5de0de8a0b3 --- /dev/null +++ b/source/blender/draw/engines/compositor/compositor_engine.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +extern DrawEngineType draw_engine_compositor_type; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c index 536242f67d8..a3ab4cdb830 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows_cascade.c +++ b/source/blender/draw/engines/eevee/eevee_shadows_cascade.c @@ -357,7 +357,7 @@ static void eevee_shadow_cascade_setup(EEVEE_LightsInfo *linfo, mul_m4_m4m4(csm_data->shadowmat[c], texcomat, viewprojmat); #ifdef DEBUG_CSM - DRW_debug_m4_as_bbox(viewprojmat, dbg_col, true); + DRW_debug_m4_as_bbox(viewprojmat, true, dbg_col); #endif } diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl index 688ae4915e1..7dec30a96b1 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_resolve_frag.glsl @@ -124,7 +124,7 @@ void dof_slight_focus_gather(float radius, out vec4 out_color, out float out_wei dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion); dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion); - /* Fix weighting issues on perfectly focus > slight focus transitionning areas. */ + /* Fix weighting issues on perfectly focus > slight focus transitioning areas. */ if (abs(center_data.coc) < 0.5) { bg_col = center_data.color; bg_weight = 1.0; diff --git a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl index 06dcbeaed66..7230758a93f 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_dof_scatter_frag.glsl @@ -67,7 +67,7 @@ void main(void) /* Occlude the sprite with geometry from the same field * using a VSM like chebychev test (slide 85). */ float mean = occlusion_data.x; - float variance = occlusion_data.x; + float variance = occlusion_data.y; shapes *= variance * safe_rcp(variance + sqr(max(cocs * correction_fac - mean, 0.0))); } diff --git a/source/blender/draw/engines/eevee_next/eevee_camera.hh b/source/blender/draw/engines/eevee_next/eevee_camera.hh index 8bf64199246..49f9b14e11b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_camera.hh +++ b/source/blender/draw/engines/eevee_next/eevee_camera.hh @@ -82,7 +82,6 @@ class Camera { private: Instance &inst_; - /** Double buffered to detect changes and have history for re-projection. */ CameraDataBuf data_; public: @@ -112,6 +111,10 @@ class Camera { { return data_.type == CAMERA_ORTHO; } + bool is_perspective() const + { + return data_.type == CAMERA_PERSP; + } const float3 &position() const { return *reinterpret_cast<const float3 *>(data_.viewinv[3]); diff --git a/source/blender/draw/engines/eevee_next/eevee_defines.hh b/source/blender/draw/engines/eevee_next/eevee_defines.hh index 1e7979b594e..96c5095317d 100644 --- a/source/blender/draw/engines/eevee_next/eevee_defines.hh +++ b/source/blender/draw/engines/eevee_next/eevee_defines.hh @@ -11,12 +11,13 @@ #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 - * thread-group which maximum size is 1024. - */ -#define CULLING_BATCH_SIZE 1024 +/* Avoid too much overhead caused by resizing the light buffers too many time. */ +#define LIGHT_CHUNK 256 + +#define CULLING_SELECT_GROUP_SIZE 256 +#define CULLING_SORT_GROUP_SIZE 256 +#define CULLING_ZBIN_GROUP_SIZE 1024 +#define CULLING_TILE_GROUP_SIZE 1024 /** * IMPORTANT: Some data packing are tweaked for these values. @@ -44,4 +45,24 @@ /* Minimum visibility size. */ #define LIGHTPROBE_FILTER_VIS_GROUP_SIZE 16 +/* Film. */ #define FILM_GROUP_SIZE 16 + +/* Motion Blur. */ +#define MOTION_BLUR_GROUP_SIZE 32 +#define MOTION_BLUR_DILATE_GROUP_SIZE 512 + +/* Depth Of Field. */ +#define DOF_TILES_SIZE 8 +#define DOF_TILES_FLATTEN_GROUP_SIZE DOF_TILES_SIZE +#define DOF_TILES_DILATE_GROUP_SIZE 8 +#define DOF_BOKEH_LUT_SIZE 32 +#define DOF_MAX_SLIGHT_FOCUS_RADIUS 5 +#define DOF_SLIGHT_FOCUS_SAMPLE_MAX 16 +#define DOF_MIP_COUNT 4 +#define DOF_REDUCE_GROUP_SIZE (1 << (DOF_MIP_COUNT - 1)) +#define DOF_DEFAULT_GROUP_SIZE 32 +#define DOF_STABILIZE_GROUP_SIZE 16 +#define DOF_FILTER_GROUP_SIZE 8 +#define DOF_GATHER_GROUP_SIZE DOF_TILES_SIZE +#define DOF_RESOLVE_GROUP_SIZE (DOF_TILES_SIZE * 2) diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc new file mode 100644 index 00000000000..3700076153e --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.cc @@ -0,0 +1,768 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Depth of field post process effect. + * + * There are 2 methods to achieve this effect. + * - The first uses projection matrix offsetting and sample accumulation to give + * reference quality depth of field. But this needs many samples to hide the + * under-sampling. + * - The second one is a post-processing based one. It follows the + * implementation described in the presentation + * "Life of a Bokeh - Siggraph 2018" from Guillaume Abadie. + * There are some difference with our actual implementation that prioritize quality. + */ + +#include "DRW_render.h" + +#include "BKE_camera.h" +#include "DNA_camera_types.h" + +#include "GPU_platform.h" +#include "GPU_texture.h" +#include "GPU_uniform_buffer.h" + +#include "eevee_camera.hh" +#include "eevee_instance.hh" +#include "eevee_sampling.hh" +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" + +#include "eevee_depth_of_field.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Depth of field + * \{ */ + +void DepthOfField::init() +{ + const SceneEEVEE &sce_eevee = inst_.scene->eevee; + const Object *camera_object_eval = inst_.camera_eval_object; + const ::Camera *camera = (camera_object_eval) ? + reinterpret_cast<const ::Camera *>(camera_object_eval->data) : + nullptr; + if (camera == nullptr) { + /* Set to invalid value for update detection */ + data_.scatter_color_threshold = -1.0f; + return; + } + /* Reminder: These are parameters not interpolated by motion blur. */ + int update = 0; + int sce_flag = sce_eevee.flag; + update += assign_if_different(do_jitter_, (sce_flag & SCE_EEVEE_DOF_JITTER) != 0); + update += assign_if_different(user_overblur_, sce_eevee.bokeh_overblur / 100.0f); + update += assign_if_different(fx_max_coc_, sce_eevee.bokeh_max_size); + update += assign_if_different(data_.scatter_color_threshold, sce_eevee.bokeh_threshold); + update += assign_if_different(data_.scatter_neighbor_max_color, sce_eevee.bokeh_neighbor_max); + update += assign_if_different(data_.bokeh_blades, float(camera->dof.aperture_blades)); + if (update > 0) { + inst_.sampling.reset(); + } +} + +void DepthOfField::sync() +{ + const Camera &camera = inst_.camera; + const Object *camera_object_eval = inst_.camera_eval_object; + const ::Camera *camera_data = (camera_object_eval) ? + reinterpret_cast<const ::Camera *>(camera_object_eval->data) : + nullptr; + + int update = 0; + + if (camera_data == nullptr || (camera_data->dof.flag & CAM_DOF_ENABLED) == 0) { + update += assign_if_different(jitter_radius_, 0.0f); + update += assign_if_different(fx_radius_, 0.0f); + if (update > 0) { + inst_.sampling.reset(); + } + return; + } + + float2 anisotropic_scale = {clamp_f(1.0f / camera_data->dof.aperture_ratio, 1e-5f, 1.0f), + clamp_f(camera_data->dof.aperture_ratio, 1e-5f, 1.0f)}; + update += assign_if_different(data_.bokeh_anisotropic_scale, anisotropic_scale); + update += assign_if_different(data_.bokeh_rotation, camera_data->dof.aperture_rotation); + update += assign_if_different(focus_distance_, + BKE_camera_object_dof_distance(camera_object_eval)); + data_.bokeh_anisotropic_scale_inv = 1.0f / data_.bokeh_anisotropic_scale; + + float fstop = max_ff(camera_data->dof.aperture_fstop, 1e-5f); + + if (update) { + inst_.sampling.reset(); + } + + float aperture = 1.0f / (2.0f * fstop); + if (camera.is_perspective()) { + aperture *= camera_data->lens * 1e-3f; + } + + if (camera.is_orthographic()) { + /* FIXME: Why is this needed? Some kind of implicit unit conversion? */ + aperture *= 0.04f; + /* Really strange behavior from Cycles but replicating. */ + focus_distance_ += camera.data_get().clip_near; + } + + if (camera.is_panoramic()) { + /* FIXME: Eyeballed. */ + aperture *= 0.185f; + } + + if (camera_data->dof.aperture_ratio < 1.0) { + /* If ratio is scaling the bokeh outwards, we scale the aperture so that + * the gather kernel size will encompass the maximum axis. */ + aperture /= max_ff(camera_data->dof.aperture_ratio, 1e-5f); + } + + float jitter_radius, fx_radius; + + /* Balance blur radius between fx dof and jitter dof. */ + if (do_jitter_ && (inst_.sampling.dof_ring_count_get() > 0) && !camera.is_panoramic() && + !inst_.is_viewport()) { + /* Compute a minimal overblur radius to fill the gaps between the samples. + * This is just the simplified form of dividing the area of the bokeh by + * the number of samples. */ + float minimal_overblur = 1.0f / sqrtf(inst_.sampling.dof_sample_count_get()); + + fx_radius = (minimal_overblur + user_overblur_) * aperture; + /* Avoid dilating the shape. Over-blur only soften. */ + jitter_radius = max_ff(0.0f, aperture - fx_radius); + } + else { + jitter_radius = 0.0f; + fx_radius = aperture; + } + + /* Disable post fx if result wouldn't be noticeable. */ + if (fx_max_coc_ <= 0.5f) { + fx_radius = 0.0f; + } + + update += assign_if_different(jitter_radius_, jitter_radius); + update += assign_if_different(fx_radius_, fx_radius); + if (update > 0) { + inst_.sampling.reset(); + } + + if (fx_radius_ == 0.0f) { + return; + } + + /* TODO(fclem): Once we render into multiple view, we will need to use the maximum resolution. */ + int2 max_render_res = inst_.film.render_extent_get(); + int2 half_res = math::divide_ceil(max_render_res, int2(2)); + int2 reduce_size = math::ceil_to_multiple(half_res, int2(DOF_REDUCE_GROUP_SIZE)); + + data_.gather_uv_fac = 1.0f / float2(reduce_size); + + /* Now that we know the maximum render resolution of every view, using depth of field, allocate + * the reduced buffers. Color needs to be signed format here. See note in shader for + * explanation. Do not use texture pool because of needs mipmaps. */ + reduced_color_tx_.ensure_2d(GPU_RGBA16F, reduce_size, nullptr, DOF_MIP_COUNT); + reduced_coc_tx_.ensure_2d(GPU_R16F, reduce_size, nullptr, DOF_MIP_COUNT); + reduced_color_tx_.ensure_mip_views(); + reduced_coc_tx_.ensure_mip_views(); + + /* Resize the scatter list to contain enough entry to cover half the screen with sprites (which + * is unlikely due to local contrast test). */ + data_.scatter_max_rect = (reduced_color_tx_.pixel_count() / 4) / 2; + scatter_fg_list_buf_.resize(data_.scatter_max_rect); + scatter_bg_list_buf_.resize(data_.scatter_max_rect); + + bokeh_lut_pass_sync(); + setup_pass_sync(); + stabilize_pass_sync(); + downsample_pass_sync(); + reduce_pass_sync(); + tiles_flatten_pass_sync(); + tiles_dilate_pass_sync(); + gather_pass_sync(); + filter_pass_sync(); + scatter_pass_sync(); + hole_fill_pass_sync(); + resolve_pass_sync(); +} + +void DepthOfField::jitter_apply(float4x4 &winmat, float4x4 &viewmat) +{ + if (jitter_radius_ == 0.0f) { + return; + } + + float radius, theta; + inst_.sampling.dof_disk_sample_get(&radius, &theta); + + if (data_.bokeh_blades >= 3.0f) { + theta = circle_to_polygon_angle(data_.bokeh_blades, theta); + radius *= circle_to_polygon_radius(data_.bokeh_blades, theta); + } + radius *= jitter_radius_; + theta += data_.bokeh_rotation; + + /* Sample in View Space. */ + float2 sample = float2(radius * cosf(theta), radius * sinf(theta)); + sample *= data_.bokeh_anisotropic_scale; + /* Convert to NDC Space. */ + float3 jitter = float3(UNPACK2(sample), -focus_distance_); + float3 center = float3(0.0f, 0.0f, -focus_distance_); + mul_project_m4_v3(winmat.ptr(), jitter); + mul_project_m4_v3(winmat.ptr(), center); + + const bool is_ortho = (winmat[2][3] != -1.0f); + if (is_ortho) { + sample *= focus_distance_; + } + /* Translate origin. */ + sub_v2_v2(viewmat[3], sample); + /* Skew winmat Z axis. */ + add_v2_v2(winmat[2], center - jitter); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Passes setup. + * \{ */ + +void DepthOfField::bokeh_lut_pass_sync() +{ + const bool has_anisotropy = data_.bokeh_anisotropic_scale != float2(1.0f); + if (!has_anisotropy && (data_.bokeh_blades == 0.0)) { + /* No need for LUTs in these cases. */ + bokeh_lut_ps_ = nullptr; + return; + } + + /* Precompute bokeh texture. */ + bokeh_lut_ps_ = DRW_pass_create("Dof.bokeh_lut_ps_", DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_BOKEH_LUT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, bokeh_lut_ps_); + DRW_shgroup_uniform_block(grp, "dof_buf", data_); + DRW_shgroup_uniform_image_ref(grp, "out_gather_lut_img", &bokeh_gather_lut_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_scatter_lut_img", &bokeh_scatter_lut_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_resolve_lut_img", &bokeh_resolve_lut_tx_); + DRW_shgroup_call_compute(grp, 1, 1, 1); +} + +void DepthOfField::setup_pass_sync() +{ + RenderBuffers &render_buffers = inst_.render_buffers; + + setup_ps_ = DRW_pass_create("Dof.setup_ps_", DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_SETUP); + DRWShadingGroup *grp = DRW_shgroup_create(sh, setup_ps_); + DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); + DRW_shgroup_uniform_block(grp, "dof_buf", data_); + DRW_shgroup_uniform_image_ref(grp, "out_color_img", &setup_color_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_coc_img", &setup_coc_tx_); + DRW_shgroup_call_compute_ref(grp, dispatch_setup_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); +} + +void DepthOfField::stabilize_pass_sync() +{ + RenderBuffers &render_buffers = inst_.render_buffers; + VelocityModule &velocity = inst_.velocity; + + stabilize_ps_ = DRW_pass_create("Dof.stabilize_ps_", DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_STABILIZE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, stabilize_ps_); + DRW_shgroup_uniform_block_ref(grp, "camera_prev", &(*velocity.camera_steps[STEP_PREVIOUS])); + DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*velocity.camera_steps[STEP_CURRENT])); + /* This is only for temporal stability. The next step is not needed. */ + DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*velocity.camera_steps[STEP_PREVIOUS])); + DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &setup_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &render_buffers.vector_tx, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "in_history_tx", &stabilize_input_, with_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); + DRW_shgroup_uniform_bool(grp, "use_history", &stabilize_valid_history_, 1); + DRW_shgroup_uniform_block(grp, "dof_buf", data_); + DRW_shgroup_uniform_image(grp, "out_coc_img", reduced_coc_tx_.mip_view(0)); + DRW_shgroup_uniform_image(grp, "out_color_img", reduced_color_tx_.mip_view(0)); + DRW_shgroup_uniform_image_ref(grp, "out_history_img", &stabilize_output_tx_); + DRW_shgroup_call_compute_ref(grp, dispatch_stabilize_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS); +} + +void DepthOfField::downsample_pass_sync() +{ + downsample_ps_ = DRW_pass_create("Dof.downsample_ps_", DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_DOWNSAMPLE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, downsample_ps_); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_.mip_view(0), no_filter); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_.mip_view(0), no_filter); + DRW_shgroup_uniform_image_ref(grp, "out_color_img", &downsample_tx_); + DRW_shgroup_call_compute_ref(grp, dispatch_downsample_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); +} + +void DepthOfField::reduce_pass_sync() +{ + reduce_ps_ = DRW_pass_create("Dof.reduce_ps_", DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_REDUCE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, reduce_ps_); + DRW_shgroup_uniform_block(grp, "dof_buf", data_); + DRW_shgroup_uniform_texture_ref_ex(grp, "downsample_tx", &downsample_tx_, no_filter); + DRW_shgroup_storage_block(grp, "scatter_fg_list_buf", scatter_fg_list_buf_); + DRW_shgroup_storage_block(grp, "scatter_bg_list_buf", scatter_bg_list_buf_); + DRW_shgroup_storage_block(grp, "scatter_fg_indirect_buf", scatter_fg_indirect_buf_); + DRW_shgroup_storage_block(grp, "scatter_bg_indirect_buf", scatter_bg_indirect_buf_); + DRW_shgroup_uniform_image(grp, "inout_color_lod0_img", reduced_color_tx_.mip_view(0)); + DRW_shgroup_uniform_image(grp, "out_color_lod1_img", reduced_color_tx_.mip_view(1)); + DRW_shgroup_uniform_image(grp, "out_color_lod2_img", reduced_color_tx_.mip_view(2)); + DRW_shgroup_uniform_image(grp, "out_color_lod3_img", reduced_color_tx_.mip_view(3)); + DRW_shgroup_uniform_image(grp, "in_coc_lod0_img", reduced_coc_tx_.mip_view(0)); + DRW_shgroup_uniform_image(grp, "out_coc_lod1_img", reduced_coc_tx_.mip_view(1)); + DRW_shgroup_uniform_image(grp, "out_coc_lod2_img", reduced_coc_tx_.mip_view(2)); + DRW_shgroup_uniform_image(grp, "out_coc_lod3_img", reduced_coc_tx_.mip_view(3)); + DRW_shgroup_call_compute_ref(grp, dispatch_reduce_size_); + /* NOTE: Command buffer barrier is done automatically by the GPU backend. */ + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_STORAGE); +} + +void DepthOfField::tiles_flatten_pass_sync() +{ + tiles_flatten_ps_ = DRW_pass_create("Dof.tiles_flatten_ps_", DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_TILES_FLATTEN); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_); + /* NOTE(fclem): We should use the reduced_coc_tx_ as it is stable, but we need the slight focus + * flag from the setup pass. A better way would be to do the brute-force in focus gather without + * this. */ + DRW_shgroup_uniform_texture_ref_ex(grp, "coc_tx", &setup_coc_tx_, no_filter); + DRW_shgroup_uniform_image_ref(grp, "out_tiles_fg_img", &tiles_fg_tx_.current()); + DRW_shgroup_uniform_image_ref(grp, "out_tiles_bg_img", &tiles_bg_tx_.current()); + DRW_shgroup_call_compute_ref(grp, dispatch_tiles_flatten_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); +} + +void DepthOfField::tiles_dilate_pass_sync() +{ + tiles_dilate_minmax_ps_ = DRW_pass_create("Dof.tiles_dilate_minmax_ps_", DRW_STATE_NO_DRAW); + tiles_dilate_minabs_ps_ = DRW_pass_create("Dof.tiles_dilate_minabs_ps_", DRW_STATE_NO_DRAW); + for (int pass = 0; pass < 2; pass++) { + DRWPass *drw_pass = (pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_; + GPUShader *sh = inst_.shaders.static_shader_get((pass == 0) ? DOF_TILES_DILATE_MINMAX : + DOF_TILES_DILATE_MINABS); + DRWShadingGroup *grp = DRW_shgroup_create(sh, drw_pass); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.previous()); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.previous()); + DRW_shgroup_uniform_image_ref(grp, "out_tiles_fg_img", &tiles_fg_tx_.current()); + DRW_shgroup_uniform_image_ref(grp, "out_tiles_bg_img", &tiles_bg_tx_.current()); + DRW_shgroup_uniform_int(grp, "ring_count", &tiles_dilate_ring_count_, 1); + DRW_shgroup_uniform_int(grp, "ring_width_multiplier", &tiles_dilate_ring_width_mul_, 1); + DRW_shgroup_call_compute_ref(grp, dispatch_tiles_dilate_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } +} + +void DepthOfField::gather_pass_sync() +{ + gather_fg_ps_ = DRW_pass_create("Dof.gather_fg_ps_", DRW_STATE_NO_DRAW); + gather_bg_ps_ = DRW_pass_create("Dof.gather_bg_ps_", DRW_STATE_NO_DRAW); + for (int pass = 0; pass < 2; pass++) { + SwapChain<TextureFromPool, 2> &color_chain = (pass == 0) ? color_fg_tx_ : color_bg_tx_; + SwapChain<TextureFromPool, 2> &weight_chain = (pass == 0) ? weight_fg_tx_ : weight_bg_tx_; + bool use_lut = bokeh_lut_ps_ != nullptr; + eShaderType sh_type = (pass == 0) ? + (use_lut ? DOF_GATHER_FOREGROUND_LUT : DOF_GATHER_FOREGROUND) : + (use_lut ? DOF_GATHER_BACKGROUND_LUT : DOF_GATHER_BACKGROUND); + GPUShader *sh = inst_.shaders.static_shader_get(sh_type); + DRWShadingGroup *grp = DRW_shgroup_create(sh, (pass == 0) ? gather_fg_ps_ : gather_bg_ps_); + inst_.sampling.bind_resources(grp); + DRW_shgroup_uniform_block(grp, "dof_buf", data_); + DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, gather_bilinear); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, gather_nearest); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, gather_nearest); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.current()); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.current()); + DRW_shgroup_uniform_image_ref(grp, "out_color_img", &color_chain.current()); + DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_chain.current()); + DRW_shgroup_uniform_image_ref(grp, "out_occlusion_img", &occlusion_tx_); + DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_gather_lut_tx_); + DRW_shgroup_call_compute_ref(grp, dispatch_gather_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + } +} + +void DepthOfField::filter_pass_sync() +{ + filter_fg_ps_ = DRW_pass_create("Dof.filter_fg_ps_", DRW_STATE_NO_DRAW); + filter_bg_ps_ = DRW_pass_create("Dof.filter_bg_ps_", DRW_STATE_NO_DRAW); + for (int pass = 0; pass < 2; pass++) { + SwapChain<TextureFromPool, 2> &color_chain = (pass == 0) ? color_fg_tx_ : color_bg_tx_; + SwapChain<TextureFromPool, 2> &weight_chain = (pass == 0) ? weight_fg_tx_ : weight_bg_tx_; + GPUShader *sh = inst_.shaders.static_shader_get(DOF_FILTER); + DRWShadingGroup *grp = DRW_shgroup_create(sh, (pass == 0) ? filter_fg_ps_ : filter_bg_ps_); + DRW_shgroup_uniform_texture_ref(grp, "color_tx", &color_chain.previous()); + DRW_shgroup_uniform_texture_ref(grp, "weight_tx", &weight_chain.previous()); + DRW_shgroup_uniform_image_ref(grp, "out_color_img", &color_chain.current()); + DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_chain.current()); + DRW_shgroup_call_compute_ref(grp, dispatch_filter_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + } +} + +void DepthOfField::scatter_pass_sync() +{ + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ADD_FULL; + scatter_fg_ps_ = DRW_pass_create("Dof.scatter_fg_ps_", state); + scatter_bg_ps_ = DRW_pass_create("Dof.scatter_bg_ps_", state); + for (int pass = 0; pass < 2; pass++) { + GPUStorageBuf *scatter_buf = (pass == 0) ? scatter_fg_indirect_buf_ : scatter_bg_indirect_buf_; + GPUStorageBuf *rect_list_buf = (pass == 0) ? scatter_fg_list_buf_ : scatter_bg_list_buf_; + + GPUShader *sh = inst_.shaders.static_shader_get(DOF_SCATTER); + DRWShadingGroup *grp = DRW_shgroup_create(sh, (pass == 0) ? scatter_fg_ps_ : scatter_bg_ps_); + DRW_shgroup_uniform_bool_copy(grp, "use_bokeh_lut", bokeh_lut_ps_ != nullptr); + DRW_shgroup_storage_block(grp, "scatter_list_buf", rect_list_buf); + DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_scatter_lut_tx_); + DRW_shgroup_uniform_texture_ref(grp, "occlusion_tx", &occlusion_tx_); + DRW_shgroup_call_procedural_indirect(grp, GPU_PRIM_TRI_STRIP, nullptr, scatter_buf); + if (pass == 0) { + /* Avoid background gather pass writing to the occlusion_tx mid pass. */ + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS); + } + } +} + +void DepthOfField::hole_fill_pass_sync() +{ + hole_fill_ps_ = DRW_pass_create("Dof.hole_fill_ps_", DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(DOF_GATHER_HOLE_FILL); + DRWShadingGroup *grp = DRW_shgroup_create(sh, hole_fill_ps_); + inst_.sampling.bind_resources(grp); + DRW_shgroup_uniform_block(grp, "dof_buf", data_); + DRW_shgroup_uniform_texture_ex(grp, "color_bilinear_tx", reduced_color_tx_, gather_bilinear); + DRW_shgroup_uniform_texture_ex(grp, "color_tx", reduced_color_tx_, gather_nearest); + DRW_shgroup_uniform_texture_ex(grp, "coc_tx", reduced_coc_tx_, gather_nearest); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.current()); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.current()); + DRW_shgroup_uniform_image_ref(grp, "out_color_img", &hole_fill_color_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &hole_fill_weight_tx_); + DRW_shgroup_call_compute_ref(grp, dispatch_gather_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); +} + +void DepthOfField::resolve_pass_sync() +{ + eGPUSamplerState with_filter = GPU_SAMPLER_FILTER; + RenderBuffers &render_buffers = inst_.render_buffers; + + resolve_ps_ = DRW_pass_create("Dof.resolve_ps_", DRW_STATE_NO_DRAW); + bool use_lut = bokeh_lut_ps_ != nullptr; + eShaderType sh_type = use_lut ? DOF_RESOLVE_LUT : DOF_RESOLVE; + GPUShader *sh = inst_.shaders.static_shader_get(sh_type); + DRWShadingGroup *grp = DRW_shgroup_create(sh, resolve_ps_); + inst_.sampling.bind_resources(grp); + DRW_shgroup_uniform_block(grp, "dof_buf", data_); + DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "color_tx", &input_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "stable_color_tx", &resolve_stable_color_tx_, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "color_bg_tx", &color_bg_tx_.current(), with_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "color_fg_tx", &color_fg_tx_.current(), with_filter); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_fg_img", &tiles_fg_tx_.current()); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_bg_img", &tiles_bg_tx_.current()); + DRW_shgroup_uniform_texture_ref(grp, "weight_bg_tx", &weight_bg_tx_.current()); + DRW_shgroup_uniform_texture_ref(grp, "weight_fg_tx", &weight_fg_tx_.current()); + DRW_shgroup_uniform_texture_ref(grp, "color_hole_fill_tx", &hole_fill_color_tx_); + DRW_shgroup_uniform_texture_ref(grp, "weight_hole_fill_tx", &hole_fill_weight_tx_); + DRW_shgroup_uniform_texture_ref(grp, "bokeh_lut_tx", &bokeh_resolve_lut_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_color_img", &output_color_tx_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + DRW_shgroup_call_compute_ref(grp, dispatch_resolve_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Post-FX Rendering. + * \{ */ + +/* Similar to Film::update_sample_table() but with constant filter radius and constant sample + * count. */ +void DepthOfField::update_sample_table() +{ + float2 subpixel_offset = inst_.film.pixel_jitter_get(); + /* Since the film jitter is in full-screen res, divide by 2 to get the jitter in half res. */ + subpixel_offset *= 0.5; + + /* Same offsets as in dof_spatial_filtering(). */ + const std::array<int2, 4> plus_offsets = {int2(-1, 0), int2(0, -1), int2(1, 0), int2(0, 1)}; + + const float radius = 1.5f; + int i = 0; + for (int2 offset : plus_offsets) { + float2 pixel_ofs = float2(offset) - subpixel_offset; + data_.filter_samples_weight[i++] = film_filter_weight(radius, math::length_squared(pixel_ofs)); + } + data_.filter_center_weight = film_filter_weight(radius, math::length_squared(subpixel_offset)); +} + +void DepthOfField::render(GPUTexture **input_tx, + GPUTexture **output_tx, + DepthOfFieldBuffer &dof_buffer) +{ + if (fx_radius_ == 0.0f) { + return; + } + + input_color_tx_ = *input_tx; + output_color_tx_ = *output_tx; + extent_ = {GPU_texture_width(input_color_tx_), GPU_texture_height(input_color_tx_)}; + + { + const CameraData &cam_data = inst_.camera.data_get(); + data_.camera_type = cam_data.type; + /* OPTI(fclem) Could be optimized. */ + float3 jitter = float3(fx_radius_, 0.0f, -focus_distance_); + float3 center = float3(0.0f, 0.0f, -focus_distance_); + mul_project_m4_v3(cam_data.winmat.ptr(), jitter); + mul_project_m4_v3(cam_data.winmat.ptr(), center); + /* Simplify CoC calculation to a simple MADD. */ + if (inst_.camera.is_orthographic()) { + data_.coc_mul = (center[0] - jitter[0]) * 0.5f * extent_[0]; + data_.coc_bias = focus_distance_ * data_.coc_mul; + } + else { + data_.coc_bias = -(center[0] - jitter[0]) * 0.5f * extent_[0]; + data_.coc_mul = focus_distance_ * data_.coc_bias; + } + + float min_fg_coc = coc_radius_from_camera_depth(data_, -cam_data.clip_near); + float max_bg_coc = coc_radius_from_camera_depth(data_, -cam_data.clip_far); + if (data_.camera_type != CAMERA_ORTHO) { + /* Background is at infinity so maximum CoC is the limit of coc_radius_from_camera_depth + * at -inf. We only do this for perspective camera since orthographic coc limit is inf. */ + max_bg_coc = data_.coc_bias; + } + /* Clamp with user defined max. */ + data_.coc_abs_max = min_ff(max_ff(fabsf(min_fg_coc), fabsf(max_bg_coc)), fx_max_coc_); + /* TODO(fclem): Make this dependent of the quality of the gather pass. */ + data_.scatter_coc_threshold = 4.0f; + + update_sample_table(); + + data_.push_update(); + } + + int2 half_res = math::divide_ceil(extent_, int2(2)); + int2 quarter_res = math::divide_ceil(extent_, int2(4)); + int2 tile_res = math::divide_ceil(half_res, int2(DOF_TILES_SIZE)); + + dispatch_setup_size_ = int3(math::divide_ceil(half_res, int2(DOF_DEFAULT_GROUP_SIZE)), 1); + dispatch_stabilize_size_ = int3(math::divide_ceil(half_res, int2(DOF_STABILIZE_GROUP_SIZE)), 1); + dispatch_downsample_size_ = int3(math::divide_ceil(quarter_res, int2(DOF_DEFAULT_GROUP_SIZE)), + 1); + dispatch_reduce_size_ = int3(math::divide_ceil(half_res, int2(DOF_REDUCE_GROUP_SIZE)), 1); + dispatch_tiles_flatten_size_ = int3(math::divide_ceil(half_res, int2(DOF_TILES_SIZE)), 1); + dispatch_tiles_dilate_size_ = int3( + math::divide_ceil(tile_res, int2(DOF_TILES_DILATE_GROUP_SIZE)), 1); + dispatch_gather_size_ = int3(math::divide_ceil(half_res, int2(DOF_GATHER_GROUP_SIZE)), 1); + dispatch_filter_size_ = int3(math::divide_ceil(half_res, int2(DOF_FILTER_GROUP_SIZE)), 1); + dispatch_resolve_size_ = int3(math::divide_ceil(extent_, int2(DOF_RESOLVE_GROUP_SIZE)), 1); + + if (GPU_type_matches_ex(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_ANY, GPU_BACKEND_OPENGL)) { + /* On Mesa, there is a sync bug which can make a portion of the main pass (usually one shader) + * leave blocks of un-initialized memory. Doing a flush seems to alleviate the issue. */ + GPU_flush(); + } + + DRW_stats_group_start("Depth of Field"); + + { + DRW_stats_group_start("Setup"); + { + bokeh_gather_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_RG16F); + bokeh_scatter_lut_tx_.acquire(int2(DOF_BOKEH_LUT_SIZE), GPU_R16F); + bokeh_resolve_lut_tx_.acquire(int2(DOF_MAX_SLIGHT_FOCUS_RADIUS * 2 + 1), GPU_R16F); + + DRW_draw_pass(bokeh_lut_ps_); + } + { + setup_color_tx_.acquire(half_res, GPU_RGBA16F); + setup_coc_tx_.acquire(half_res, GPU_R16F); + + DRW_draw_pass(setup_ps_); + } + { + stabilize_output_tx_.acquire(half_res, GPU_RGBA16F); + stabilize_valid_history_ = !dof_buffer.stabilize_history_tx_.ensure_2d(GPU_RGBA16F, + half_res); + + if (stabilize_valid_history_ == false) { + /* Avoid uninitialized memory that can contain NaNs. */ + dof_buffer.stabilize_history_tx_.clear(float4(0.0f)); + } + + stabilize_input_ = dof_buffer.stabilize_history_tx_; + /* Outputs to reduced_*_tx_ mip 0. */ + DRW_draw_pass(stabilize_ps_); + + /* WATCH(fclem): Swap Texture an TextureFromPool internal GPUTexture in order to reuse + * the one that we just consumed. */ + TextureFromPool::swap(stabilize_output_tx_, dof_buffer.stabilize_history_tx_); + + /* Used by stabilize pass. */ + stabilize_output_tx_.release(); + setup_color_tx_.release(); + } + { + DRW_stats_group_start("Tile Prepare"); + + /* WARNING: If format changes, make sure dof_tile_* GLSL constants are properly encoded. */ + tiles_fg_tx_.previous().acquire(tile_res, GPU_R11F_G11F_B10F); + tiles_bg_tx_.previous().acquire(tile_res, GPU_R11F_G11F_B10F); + tiles_fg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F); + tiles_bg_tx_.current().acquire(tile_res, GPU_R11F_G11F_B10F); + + DRW_draw_pass(tiles_flatten_ps_); + + /* Used by tile_flatten and stabilize_ps pass. */ + setup_coc_tx_.release(); + + /* Error introduced by gather center jittering. */ + const float error_multiplier = 1.0f + 1.0f / (DOF_GATHER_RING_COUNT + 0.5f); + int dilation_end_radius = ceilf((fx_max_coc_ * error_multiplier) / (DOF_TILES_SIZE * 2)); + + /* Run dilation twice. One for minmax and one for minabs. */ + for (int pass = 0; pass < 2; pass++) { + /* This algorithm produce the exact dilation radius by dividing it in multiple passes. */ + int dilation_radius = 0; + while (dilation_radius < dilation_end_radius) { + int remainder = dilation_end_radius - dilation_radius; + /* Do not step over any unvisited tile. */ + int max_multiplier = dilation_radius + 1; + + int ring_count = min_ii(DOF_DILATE_RING_COUNT, ceilf(remainder / (float)max_multiplier)); + int multiplier = min_ii(max_multiplier, floorf(remainder / (float)ring_count)); + + dilation_radius += ring_count * multiplier; + + tiles_dilate_ring_count_ = ring_count; + tiles_dilate_ring_width_mul_ = multiplier; + + tiles_fg_tx_.swap(); + tiles_bg_tx_.swap(); + + DRW_draw_pass((pass == 0) ? tiles_dilate_minmax_ps_ : tiles_dilate_minabs_ps_); + } + } + + tiles_fg_tx_.previous().release(); + tiles_bg_tx_.previous().release(); + + DRW_stats_group_end(); + } + + downsample_tx_.acquire(quarter_res, GPU_RGBA16F); + + DRW_draw_pass(downsample_ps_); + + scatter_fg_indirect_buf_.clear_to_zero(); + scatter_bg_indirect_buf_.clear_to_zero(); + + DRW_draw_pass(reduce_ps_); + + /* Used by reduce pass. */ + downsample_tx_.release(); + + DRW_stats_group_end(); + } + + for (int is_background = 0; is_background < 2; is_background++) { + DRW_stats_group_start(is_background ? "Background Convolution" : "Foreground Convolution"); + + SwapChain<TextureFromPool, 2> &color_tx = is_background ? color_bg_tx_ : color_fg_tx_; + SwapChain<TextureFromPool, 2> &weight_tx = is_background ? weight_bg_tx_ : weight_fg_tx_; + Framebuffer &scatter_fb = is_background ? scatter_bg_fb_ : scatter_fg_fb_; + DRWPass *gather_ps = is_background ? gather_bg_ps_ : gather_fg_ps_; + DRWPass *filter_ps = is_background ? filter_bg_ps_ : filter_fg_ps_; + DRWPass *scatter_ps = is_background ? scatter_bg_ps_ : scatter_fg_ps_; + + color_tx.current().acquire(half_res, GPU_RGBA16F); + weight_tx.current().acquire(half_res, GPU_R16F); + occlusion_tx_.acquire(half_res, GPU_RG16F); + + DRW_draw_pass(gather_ps); + + { + /* Filtering pass. */ + color_tx.swap(); + weight_tx.swap(); + + color_tx.current().acquire(half_res, GPU_RGBA16F); + weight_tx.current().acquire(half_res, GPU_R16F); + + DRW_draw_pass(filter_ps); + + color_tx.previous().release(); + weight_tx.previous().release(); + } + + GPU_memory_barrier(GPU_BARRIER_FRAMEBUFFER); + + scatter_fb.ensure(GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(color_tx.current())); + + GPU_framebuffer_bind(scatter_fb); + DRW_draw_pass(scatter_ps); + + /* Used by scatter pass. */ + occlusion_tx_.release(); + + DRW_stats_group_end(); + } + { + DRW_stats_group_start("Hole Fill"); + + bokeh_gather_lut_tx_.release(); + bokeh_scatter_lut_tx_.release(); + + hole_fill_color_tx_.acquire(half_res, GPU_RGBA16F); + hole_fill_weight_tx_.acquire(half_res, GPU_R16F); + + DRW_draw_pass(hole_fill_ps_); + + /* NOTE: We do not filter the hole-fill pass as effect is likely to not be noticeable. */ + + DRW_stats_group_end(); + } + { + DRW_stats_group_start("Resolve"); + + resolve_stable_color_tx_ = dof_buffer.stabilize_history_tx_; + + DRW_draw_pass(resolve_ps_); + + color_bg_tx_.current().release(); + color_fg_tx_.current().release(); + weight_bg_tx_.current().release(); + weight_fg_tx_.current().release(); + tiles_fg_tx_.current().release(); + tiles_bg_tx_.current().release(); + hole_fill_color_tx_.release(); + hole_fill_weight_tx_.release(); + bokeh_resolve_lut_tx_.release(); + + DRW_stats_group_end(); + } + + DRW_stats_group_end(); + + /* Swap buffers so that next effect has the right input. */ + SWAP(GPUTexture *, *input_tx, *output_tx); +} + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh new file mode 100644 index 00000000000..8c291b241bd --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_depth_of_field.hh @@ -0,0 +1,195 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Depth of field post process effect. + * + * There are 2 methods to achieve this effect. + * - The first uses projection matrix offsetting and sample accumulation to give + * reference quality depth of field. But this needs many samples to hide the + * under-sampling. + * - The second one is a post-processing based one. It follows the + * implementation described in the presentation + * "Life of a Bokeh - Siggraph 2018" from Guillaume Abadie. + * There are some difference with our actual implementation that prioritize quality. + */ + +#pragma once + +#include "eevee_shader_shared.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Depth of field + * \{ */ + +struct DepthOfFieldBuffer { + /** + * Per view history texture for stabilize pass. + * Swapped with stabilize_output_tx_ in order to reuse the previous history during DoF + * processing. + * Note this should be private as its inner working only concerns the Depth Of Field + * implementation. The view itself should not touch it. + */ + Texture stabilize_history_tx_ = {"dof_taa"}; +}; + +class DepthOfField { + private: + class Instance &inst_; + + /** Samplers */ + static constexpr eGPUSamplerState gather_bilinear = GPU_SAMPLER_MIPMAP | GPU_SAMPLER_FILTER; + static constexpr eGPUSamplerState gather_nearest = GPU_SAMPLER_MIPMAP; + + /** Input/Output texture references. */ + GPUTexture *input_color_tx_ = nullptr; + GPUTexture *output_color_tx_ = nullptr; + + /** Bokeh LUT precompute pass. */ + TextureFromPool bokeh_gather_lut_tx_ = {"dof_bokeh_gather_lut"}; + TextureFromPool bokeh_resolve_lut_tx_ = {"dof_bokeh_resolve_lut"}; + TextureFromPool bokeh_scatter_lut_tx_ = {"dof_bokeh_scatter_lut"}; + DRWPass *bokeh_lut_ps_ = nullptr; + + /** Outputs half-resolution color and Circle Of Confusion. */ + TextureFromPool setup_coc_tx_ = {"dof_setup_coc"}; + TextureFromPool setup_color_tx_ = {"dof_setup_color"}; + int3 dispatch_setup_size_ = int3(-1); + DRWPass *setup_ps_ = nullptr; + + /** Allocated because we need mip chain. Which isn't supported by TextureFromPool. */ + Texture reduced_coc_tx_ = {"dof_reduced_coc"}; + Texture reduced_color_tx_ = {"dof_reduced_color"}; + + /** Stabilization (flicker attenuation) of Color and CoC output of the setup pass. */ + TextureFromPool stabilize_output_tx_ = {"dof_taa"}; + GPUTexture *stabilize_input_ = nullptr; + bool1 stabilize_valid_history_ = false; + int3 dispatch_stabilize_size_ = int3(-1); + DRWPass *stabilize_ps_ = nullptr; + + /** 1/4th res color buffer used to speedup the local contrast test in the first reduce pass. */ + TextureFromPool downsample_tx_ = {"dof_downsample"}; + int3 dispatch_downsample_size_ = int3(-1); + DRWPass *downsample_ps_ = nullptr; + + /** Create mip-mapped color & COC textures for gather passes as well as scatter rect list. */ + DepthOfFieldScatterListBuf scatter_fg_list_buf_; + DepthOfFieldScatterListBuf scatter_bg_list_buf_; + DrawIndirectBuf scatter_fg_indirect_buf_; + DrawIndirectBuf scatter_bg_indirect_buf_; + int3 dispatch_reduce_size_ = int3(-1); + DRWPass *reduce_ps_ = nullptr; + + /** Outputs min & max COC in each 8x8 half res pixel tiles (so 1/16th of full resolution). */ + SwapChain<TextureFromPool, 2> tiles_fg_tx_; + SwapChain<TextureFromPool, 2> tiles_bg_tx_; + int3 dispatch_tiles_flatten_size_ = int3(-1); + DRWPass *tiles_flatten_ps_ = nullptr; + + /** Dilates the min & max CoCs to cover maximum COC values. */ + int tiles_dilate_ring_count_ = -1; + int tiles_dilate_ring_width_mul_ = -1; + int3 dispatch_tiles_dilate_size_ = int3(-1); + DRWPass *tiles_dilate_minmax_ps_ = nullptr; + DRWPass *tiles_dilate_minabs_ps_ = nullptr; + + /** Gather convolution for low intensity pixels and low contrast areas. */ + SwapChain<TextureFromPool, 2> color_bg_tx_; + SwapChain<TextureFromPool, 2> color_fg_tx_; + SwapChain<TextureFromPool, 2> weight_bg_tx_; + SwapChain<TextureFromPool, 2> weight_fg_tx_; + TextureFromPool occlusion_tx_ = {"dof_occlusion"}; + int3 dispatch_gather_size_ = int3(-1); + DRWPass *gather_fg_ps_ = nullptr; + DRWPass *gather_bg_ps_ = nullptr; + + /** Hole-fill convolution: Gather pass meant to fill areas of foreground dis-occlusion. */ + TextureFromPool hole_fill_color_tx_ = {"dof_color_hole_fill"}; + TextureFromPool hole_fill_weight_tx_ = {"dof_weight_hole_fill"}; + DRWPass *hole_fill_ps_ = nullptr; + + /** Small Filter pass to reduce noise out of gather passes. */ + int3 dispatch_filter_size_ = int3(-1); + DRWPass *filter_fg_ps_ = nullptr; + DRWPass *filter_bg_ps_ = nullptr; + + /** Scatter convolution: A quad is emitted for every 4 bright enough half pixels. */ + Framebuffer scatter_fg_fb_ = {"dof_scatter_fg"}; + Framebuffer scatter_bg_fb_ = {"dof_scatter_bg"}; + DRWPass *scatter_fg_ps_ = nullptr; + DRWPass *scatter_bg_ps_ = nullptr; + + /** Recombine the results and also perform a slight out of focus gather. */ + GPUTexture *resolve_stable_color_tx_ = nullptr; + int3 dispatch_resolve_size_ = int3(-1); + DRWPass *resolve_ps_ = nullptr; + + DepthOfFieldDataBuf data_; + + /** Scene settings that are immutable. */ + float user_overblur_; + float fx_max_coc_; + /** Use jittered depth of field where we randomize camera location. */ + bool do_jitter_; + + /** Circle of Confusion radius for FX DoF passes. Is in view X direction in [0..1] range. */ + float fx_radius_; + /** Circle of Confusion radius for jittered DoF. Is in view X direction in [0..1] range. */ + float jitter_radius_; + /** Focus distance in view space. */ + float focus_distance_; + /** Extent of the input buffer. */ + int2 extent_; + + public: + DepthOfField(Instance &inst) : inst_(inst){}; + ~DepthOfField(){}; + + void init(); + + void sync(); + + /** + * Apply Depth Of Field jittering to the view and projection matrices.. + */ + void jitter_apply(float4x4 &winmat, float4x4 &viewmat); + + /** + * Will swap input and output texture if rendering happens. The actual output of this function + * is in input_tx. + */ + void render(GPUTexture **input_tx, GPUTexture **output_tx, DepthOfFieldBuffer &dof_buffer); + + bool postfx_enabled() const + { + return fx_radius_ > 0.0f; + } + + private: + void bokeh_lut_pass_sync(); + void setup_pass_sync(); + void stabilize_pass_sync(); + void downsample_pass_sync(); + void reduce_pass_sync(); + void tiles_flatten_pass_sync(); + void tiles_dilate_pass_sync(); + void gather_pass_sync(); + void filter_pass_sync(); + void scatter_pass_sync(); + void hole_fill_pass_sync(); + void resolve_pass_sync(); + + void update_sample_table(); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_film.cc b/source/blender/draw/engines/eevee_next/eevee_film.cc index 49f43265aa8..b3fbe088471 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.cc +++ b/source/blender/draw/engines/eevee_next/eevee_film.cc @@ -183,20 +183,17 @@ void Film::init(const int2 &extent, const rcti *output_rect) * Using the render pass ensure we store the center depth. */ render_passes |= EEVEE_RENDER_PASS_Z; } - /* TEST */ - render_passes |= EEVEE_RENDER_PASS_VECTOR; } else { /* Render Case. */ render_passes = eViewLayerEEVEEPassType(inst_.view_layer->eevee.render_passes); - render_passes |= EEVEE_RENDER_PASS_COMBINED; - #define ENABLE_FROM_LEGACY(name_legacy, name_eevee) \ SET_FLAG_FROM_TEST(render_passes, \ (inst_.view_layer->passflag & SCE_PASS_##name_legacy) != 0, \ EEVEE_RENDER_PASS_##name_eevee); + ENABLE_FROM_LEGACY(COMBINED, COMBINED) ENABLE_FROM_LEGACY(Z, Z) ENABLE_FROM_LEGACY(MIST, MIST) ENABLE_FROM_LEGACY(NORMAL, NORMAL) @@ -209,6 +206,7 @@ void Film::init(const int2 &extent, const rcti *output_rect) ENABLE_FROM_LEGACY(DIFFUSE_DIRECT, DIFFUSE_LIGHT) ENABLE_FROM_LEGACY(GLOSSY_DIRECT, SPECULAR_LIGHT) ENABLE_FROM_LEGACY(ENVIRONMENT, ENVIRONMENT) + ENABLE_FROM_LEGACY(VECTOR, VECTOR) #undef ENABLE_FROM_LEGACY } @@ -216,6 +214,11 @@ void Film::init(const int2 &extent, const rcti *output_rect) /* Filter obsolete passes. */ render_passes &= ~(EEVEE_RENDER_PASS_UNUSED_8 | EEVEE_RENDER_PASS_BLOOM); + if (scene_eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) { + /* Disable motion vector pass if motion blur is enabled. */ + render_passes &= ~EEVEE_RENDER_PASS_VECTOR; + } + /* TODO(@fclem): Can't we rely on depsgraph update notification? */ if (assign_if_different(enabled_passes_, render_passes)) { sampling.reset(); @@ -383,7 +386,7 @@ void Film::sync() DRW_shgroup_uniform_block_ref(grp, "camera_curr", &(*velocity.camera_steps[STEP_CURRENT])); DRW_shgroup_uniform_block_ref(grp, "camera_next", &(*velocity.camera_steps[step_next])); DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &rbuffers.depth_tx); - DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &rbuffers.combined_tx); + DRW_shgroup_uniform_texture_ref(grp, "combined_tx", &combined_final_tx_); DRW_shgroup_uniform_texture_ref(grp, "normal_tx", &rbuffers.normal_tx); DRW_shgroup_uniform_texture_ref(grp, "vector_tx", &rbuffers.vector_tx); DRW_shgroup_uniform_texture_ref(grp, "diffuse_light_tx", &rbuffers.diffuse_light_tx); @@ -400,10 +403,10 @@ void Film::sync() /* NOTE(@fclem): 16 is the max number of sampled texture in many implementations. * If we need more, we need to pack more of the similar passes in the same textures as arrays or * use image binding instead. */ - DRW_shgroup_uniform_image_ref(grp, "in_weight_img", &weight_src_tx_); - DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_dst_tx_); - DRW_shgroup_uniform_texture_ref_ex(grp, "in_combined_tx", &combined_src_tx_, filter); - DRW_shgroup_uniform_image_ref(grp, "out_combined_img", &combined_dst_tx_); + DRW_shgroup_uniform_image_ref(grp, "in_weight_img", &weight_tx_.current()); + DRW_shgroup_uniform_image_ref(grp, "out_weight_img", &weight_tx_.next()); + DRW_shgroup_uniform_texture_ref_ex(grp, "in_combined_tx", &combined_tx_.current(), filter); + DRW_shgroup_uniform_image_ref(grp, "out_combined_img", &combined_tx_.next()); DRW_shgroup_uniform_image_ref(grp, "depth_img", &depth_tx_); DRW_shgroup_uniform_image_ref(grp, "color_accum_img", &color_accum_tx_); DRW_shgroup_uniform_image_ref(grp, "value_accum_img", &value_accum_tx_); @@ -458,6 +461,10 @@ float2 Film::pixel_jitter_get() const eViewLayerEEVEEPassType Film::enabled_passes_get() const { + if (inst_.is_viewport() && data_.use_reprojection) { + /* Enable motion vector rendering but not the accumulation buffer. */ + return enabled_passes_ | EEVEE_RENDER_PASS_VECTOR; + } return enabled_passes_; } @@ -476,7 +483,7 @@ void Film::update_sample_table() data_.samples_weight_total = 1.0f; data_.samples_len = 1; } - /* NOTE: Threshold determined by hand until we don't hit the assert bellow. */ + /* NOTE: Threshold determined by hand until we don't hit the assert below. */ else if (data_.filter_radius < 2.20f) { /* Small filter Size. */ int closest_index = 0; @@ -538,7 +545,7 @@ void Film::update_sample_table() } } -void Film::accumulate(const DRWView *view) +void Film::accumulate(const DRWView *view, GPUTexture *combined_final_tx) { if (inst_.is_viewport()) { DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); @@ -554,11 +561,7 @@ void Film::accumulate(const DRWView *view) update_sample_table(); - /* Need to update the static references as there could have change from a previous swap. */ - weight_src_tx_ = weight_tx_.current(); - weight_dst_tx_ = weight_tx_.next(); - combined_src_tx_ = combined_tx_.current(); - combined_dst_tx_ = combined_tx_.next(); + combined_final_tx_ = combined_final_tx; data_.display_only = false; data_.push_update(); @@ -580,17 +583,13 @@ void Film::display() BLI_assert(inst_.is_viewport()); /* Acquire dummy render buffers for correct binding. They will not be used. */ - inst_.render_buffers.acquire(int2(1), (void *)this); + inst_.render_buffers.acquire(int2(1)); DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); GPU_framebuffer_bind(dfbl->default_fb); GPU_framebuffer_viewport_set(dfbl->default_fb, UNPACK2(data_.offset), UNPACK2(data_.extent)); - /* Need to update the static references as there could have change from a previous swap. */ - weight_src_tx_ = weight_tx_.current(); - weight_dst_tx_ = weight_tx_.next(); - combined_src_tx_ = combined_tx_.current(); - combined_dst_tx_ = combined_tx_.next(); + combined_final_tx_ = inst_.render_buffers.combined_tx; data_.display_only = true; data_.push_update(); diff --git a/source/blender/draw/engines/eevee_next/eevee_film.hh b/source/blender/draw/engines/eevee_next/eevee_film.hh index 1165b9a4c12..3e368782d31 100644 --- a/source/blender/draw/engines/eevee_next/eevee_film.hh +++ b/source/blender/draw/engines/eevee_next/eevee_film.hh @@ -40,6 +40,9 @@ class Film { private: Instance &inst_; + /** Incoming combined buffer with post FX applied (motion blur + depth of field). */ + GPUTexture *combined_final_tx_ = nullptr; + /** Main accumulation textures containing every render-pass except depth and combined. */ Texture color_accum_tx_; Texture value_accum_tx_; @@ -47,14 +50,8 @@ class Film { Texture depth_tx_; /** Combined "Color" buffer. Double buffered to allow re-projection. */ SwapChain<Texture, 2> combined_tx_; - /** Static reference as SwapChain does not actually move the objects when swapping. */ - GPUTexture *combined_src_tx_ = nullptr; - GPUTexture *combined_dst_tx_ = nullptr; /** Weight buffers. Double buffered to allow updating it during accumulation. */ SwapChain<Texture, 2> weight_tx_; - /** Static reference as SwapChain does not actually move the objects when swapping. */ - GPUTexture *weight_src_tx_ = nullptr; - GPUTexture *weight_dst_tx_ = nullptr; /** User setting to disable reprojection. Useful for debugging or have a more precise render. */ bool force_disable_reprojection_ = false; @@ -74,7 +71,7 @@ class Film { void end_sync(); /** Accumulate the newly rendered sample contained in #RenderBuffers and blit to display. */ - void accumulate(const DRWView *view); + void accumulate(const DRWView *view, GPUTexture *combined_final_tx); /** Blit to display. No rendered sample needed. */ void display(); @@ -82,6 +79,7 @@ class Film { float *read_pass(eViewLayerEEVEEPassType pass_type); float *read_aov(ViewLayerAOV *aov); + /** Returns shading views internal resolution. */ int2 render_extent_get() const { return data_.render_extent; diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.cc b/source/blender/draw/engines/eevee_next/eevee_instance.cc index 9f8cf6dc6ba..57786adb657 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.cc +++ b/source/blender/draw/engines/eevee_next/eevee_instance.cc @@ -53,6 +53,10 @@ void Instance::init(const int2 &output_res, v3d = v3d_; rv3d = rv3d_; + if (assign_if_different(debug_mode, (eDebugMode)G.debug_value)) { + sampling.reset(); + } + info = ""; update_eval_members(); @@ -60,6 +64,9 @@ void Instance::init(const int2 &output_res, sampling.init(scene); camera.init(); film.init(output_res, output_rect); + velocity.init(); + depth_of_field.init(); + motion_blur.init(); main_view.init(); } @@ -92,21 +99,22 @@ void Instance::update_eval_members() void Instance::begin_sync() { materials.begin_sync(); - velocity.begin_sync(); + velocity.begin_sync(); /* NOTE: Also syncs camera. */ + lights.begin_sync(); gpencil_engine_enabled = false; - render_buffers.sync(); + depth_of_field.sync(); + motion_blur.sync(); pipelines.sync(); main_view.sync(); world.sync(); - camera.sync(); film.sync(); } void Instance::object_sync(Object *ob) { - const bool is_renderable_type = ELEM(ob->type, OB_CURVES, OB_GPENCIL, OB_MESH); + const bool is_renderable_type = ELEM(ob->type, OB_CURVES, OB_GPENCIL, OB_MESH, OB_LAMP); const int ob_visibility = DRW_object_visibility_in_active_context(ob); const bool partsys_is_visible = (ob_visibility & OB_VISIBLE_PARTICLES) != 0 && (ob->type == OB_MESH); @@ -130,6 +138,7 @@ void Instance::object_sync(Object *ob) if (object_is_visible) { switch (ob->type) { case OB_LAMP: + lights.sync_light(ob, ob_handle); break; case OB_MESH: case OB_CURVES_LEGACY: @@ -169,6 +178,7 @@ void Instance::object_sync_render(void *instance_, void Instance::end_sync() { velocity.end_sync(); + lights.end_sync(); sampling.end_sync(); film.end_sync(); } @@ -212,22 +222,12 @@ void Instance::render_sample() sampling.step(); main_view.render(); -} - -/** \} */ -/* -------------------------------------------------------------------- */ -/** \name Interface - * \{ */ + motion_blur.step(); +} -void Instance::render_frame(RenderLayer *render_layer, const char *view_name) +void Instance::render_read_result(RenderLayer *render_layer, const char *view_name) { - while (!sampling.finished()) { - this->render_sample(); - /* TODO(fclem) print progression. */ - } - - /* Read Results. */ eViewLayerEEVEEPassType pass_bits = film.enabled_passes_get(); for (auto i : IndexRange(EEVEE_RENDER_PASS_MAX_BIT)) { eViewLayerEEVEEPassType pass_type = eViewLayerEEVEEPassType(pass_bits & (1 << i)); @@ -240,7 +240,6 @@ void Instance::render_frame(RenderLayer *render_layer, const char *view_name) if (rp) { float *result = film.read_pass(pass_type); if (result) { - std::cout << "read " << pass_name << std::endl; BLI_mutex_lock(&render->update_render_passes_mutex); /* WORKAROUND: We use texture read to avoid using a framebuffer to get the render result. * However, on some implementation, we need a buffer with a few extra bytes for the read to @@ -252,6 +251,45 @@ void Instance::render_frame(RenderLayer *render_layer, const char *view_name) } } } + + /* The vector pass is initialized to weird values. Set it to neutral value if not rendered. */ + if ((pass_bits & EEVEE_RENDER_PASS_VECTOR) == 0) { + const char *vector_pass_name = Film::pass_to_render_pass_name(EEVEE_RENDER_PASS_VECTOR); + RenderPass *vector_rp = RE_pass_find_by_name(render_layer, vector_pass_name, view_name); + if (vector_rp) { + memset(vector_rp->rect, 0, sizeof(float) * 4 * vector_rp->rectx * vector_rp->recty); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Interface + * \{ */ + +void Instance::render_frame(RenderLayer *render_layer, const char *view_name) +{ + while (!sampling.finished()) { + this->render_sample(); + + /* TODO(fclem) print progression. */ +#if 0 + /* TODO(fclem): Does not currently work. But would be better to just display to 2D view like + * cycles does. */ + if (G.background == false && first_read) { + /* Allow to preview the first sample. */ + /* TODO(fclem): Might want to not do this during animation render to avoid too much stall. */ + this->render_read_result(render_layer, view_name); + first_read = false; + DRW_render_context_disable(render->re); + /* Allow the 2D viewport to grab the ticket mutex to display the render. */ + DRW_render_context_enable(render->re); + } +#endif + } + + this->render_read_result(render_layer, view_name); } void Instance::draw_viewport(DefaultFramebufferList *dfbl) @@ -260,7 +298,10 @@ void Instance::draw_viewport(DefaultFramebufferList *dfbl) render_sample(); velocity.step_swap(); - if (!sampling.finished_viewport()) { + /* Do not request redraw during viewport animation to lock the framerate to the animation + * playback rate. This is in order to preserve motion blur aspect and also to avoid TAA reset + * that can show flickering. */ + if (!sampling.finished_viewport() && !DRW_state_is_playback()) { DRW_viewport_request_redraw(); } diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index 1efda769648..d52e4a8e43b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -16,8 +16,11 @@ #include "DRW_render.h" #include "eevee_camera.hh" +#include "eevee_depth_of_field.hh" #include "eevee_film.hh" +#include "eevee_light.hh" #include "eevee_material.hh" +#include "eevee_motion_blur.hh" #include "eevee_pipeline.hh" #include "eevee_renderbuffers.hh" #include "eevee_sampling.hh" @@ -34,13 +37,17 @@ namespace blender::eevee { */ class Instance { friend VelocityModule; + friend MotionBlurModule; public: ShaderModule &shaders; SyncModule sync; MaterialModule materials; PipelineModule pipelines; + LightModule lights; VelocityModule velocity; + MotionBlurModule motion_blur; + DepthOfField depth_of_field; Sampling sampling; Camera camera; Film film; @@ -66,8 +73,10 @@ class Instance { /** True if the grease pencil engine might be running. */ bool gpencil_engine_enabled; - /* Info string displayed at the top of the render / viewport. */ + /** Info string displayed at the top of the render / viewport. */ std::string info = ""; + /** Debug mode from debug value. */ + eDebugMode debug_mode = eDebugMode::DEBUG_NONE; public: Instance() @@ -75,7 +84,10 @@ class Instance { sync(*this), materials(*this), pipelines(*this), + lights(*this), velocity(*this), + motion_blur(*this), + depth_of_field(*this), sampling(*this), camera(*this), film(*this), @@ -138,6 +150,7 @@ class Instance { RenderEngine *engine, Depsgraph *depsgraph); void render_sample(); + void render_read_result(RenderLayer *render_layer, const char *view_name); void mesh_sync(Object *ob, ObjectHandle &ob_handle); diff --git a/source/blender/draw/engines/eevee_next/eevee_light.cc b/source/blender/draw/engines/eevee_next/eevee_light.cc new file mode 100644 index 00000000000..dbbf481f3f4 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_light.cc @@ -0,0 +1,499 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The light module manages light data buffers and light culling system. + */ + +#include "draw_debug.hh" + +#include "eevee_instance.hh" + +#include "eevee_light.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name LightData + * \{ */ + +static eLightType to_light_type(short blender_light_type, short blender_area_type) +{ + switch (blender_light_type) { + default: + case LA_LOCAL: + return LIGHT_POINT; + case LA_SUN: + return LIGHT_SUN; + case LA_SPOT: + return LIGHT_SPOT; + case LA_AREA: + return ELEM(blender_area_type, LA_AREA_DISK, LA_AREA_ELLIPSE) ? LIGHT_ELLIPSE : LIGHT_RECT; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Object + * \{ */ + +void Light::sync(/* ShadowModule &shadows , */ const Object *ob, float threshold) +{ + const ::Light *la = (const ::Light *)ob->data; + float scale[3]; + + float max_power = max_fff(la->r, la->g, la->b) * fabsf(la->energy / 100.0f); + float surface_max_power = max_ff(la->diff_fac, la->spec_fac) * max_power; + float volume_max_power = la->volume_fac * max_power; + + float influence_radius_surface = attenuation_radius_get(la, threshold, surface_max_power); + float influence_radius_volume = attenuation_radius_get(la, threshold, volume_max_power); + + this->influence_radius_max = max_ff(influence_radius_surface, influence_radius_volume); + this->influence_radius_invsqr_surface = 1.0f / square_f(max_ff(influence_radius_surface, 1e-8f)); + this->influence_radius_invsqr_volume = 1.0f / square_f(max_ff(influence_radius_volume, 1e-8f)); + + this->color = float3(&la->r) * la->energy; + normalize_m4_m4_ex(this->object_mat.ptr(), ob->obmat, scale); + /* Make sure we have consistent handedness (in case of negatively scaled Z axis). */ + float3 cross = math::cross(float3(this->_right), float3(this->_up)); + if (math::dot(cross, float3(this->_back)) < 0.0f) { + negate_v3(this->_up); + } + + shape_parameters_set(la, scale); + + float shape_power = shape_power_get(la); + float point_power = point_power_get(la); + this->diffuse_power = la->diff_fac * shape_power; + this->transmit_power = la->diff_fac * point_power; + this->specular_power = la->spec_fac * shape_power; + this->volume_power = la->volume_fac * point_power; + + eLightType new_type = to_light_type(la->type, la->area_shape); + if (this->type != new_type) { + /* shadow_discard_safe(shadows); */ + this->type = new_type; + } + +#if 0 + if (la->mode & LA_SHADOW) { + if (la->type == LA_SUN) { + if (this->shadow_id == LIGHT_NO_SHADOW) { + this->shadow_id = shadows.directionals.alloc(); + } + + ShadowDirectional &shadow = shadows.directionals[this->shadow_id]; + shadow.sync(this->object_mat, la->bias * 0.05f, 1.0f); + } + else { + float cone_aperture = DEG2RAD(360.0); + if (la->type == LA_SPOT) { + cone_aperture = min_ff(DEG2RAD(179.9), la->spotsize); + } + else if (la->type == LA_AREA) { + cone_aperture = DEG2RAD(179.9); + } + + if (this->shadow_id == LIGHT_NO_SHADOW) { + this->shadow_id = shadows.punctuals.alloc(); + } + + ShadowPunctual &shadow = shadows.punctuals[this->shadow_id]; + shadow.sync(this->type, + this->object_mat, + cone_aperture, + la->clipsta, + this->influence_radius_max, + la->bias * 0.05f); + } + } + else { + shadow_discard_safe(shadows); + } +#endif + + this->initialized = true; +} + +#if 0 +void Light::shadow_discard_safe(ShadowModule &shadows) +{ + if (shadow_id != LIGHT_NO_SHADOW) { + if (this->type != LIGHT_SUN) { + shadows.punctuals.free(shadow_id); + } + else { + shadows.directionals.free(shadow_id); + } + shadow_id = LIGHT_NO_SHADOW; + } +} +#endif + +/* Returns attenuation radius inverted & squared for easy bound checking inside the shader. */ +float Light::attenuation_radius_get(const ::Light *la, float light_threshold, float light_power) +{ + if (la->type == LA_SUN) { + return (light_power > 1e-5f) ? 1e16f : 0.0f; + } + + if (la->mode & LA_CUSTOM_ATTENUATION) { + return la->att_dist; + } + /* Compute the distance (using the inverse square law) + * at which the light power reaches the light_threshold. */ + /* TODO take area light scale into account. */ + return sqrtf(light_power / light_threshold); +} + +void Light::shape_parameters_set(const ::Light *la, const float scale[3]) +{ + if (la->type == LA_AREA) { + float area_size_y = (ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE)) ? la->area_sizey : + la->area_size; + _area_size_x = max_ff(0.003f, la->area_size * scale[0] * 0.5f); + _area_size_y = max_ff(0.003f, area_size_y * scale[1] * 0.5f); + /* For volume point lighting. */ + radius_squared = max_ff(0.001f, hypotf(_area_size_x, _area_size_y) * 0.5f); + radius_squared = square_f(radius_squared); + } + else { + if (la->type == LA_SPOT) { + /* Spot size & blend */ + spot_size_inv[0] = scale[2] / scale[0]; + spot_size_inv[1] = scale[2] / scale[1]; + float spot_size = cosf(la->spotsize * 0.5f); + float spot_blend = (1.0f - spot_size) * la->spotblend; + _spot_mul = 1.0f / max_ff(1e-8f, spot_blend); + _spot_bias = -spot_size * _spot_mul; + spot_tan = tanf(min_ff(la->spotsize * 0.5f, M_PI_2 - 0.0001f)); + } + + if (la->type == LA_SUN) { + _area_size_x = tanf(min_ff(la->sun_angle, DEG2RADF(179.9f)) / 2.0f); + } + else { + _area_size_x = la->area_size; + } + _area_size_y = _area_size_x = max_ff(0.001f, _area_size_x); + radius_squared = square_f(_area_size_x); + } +} + +float Light::shape_power_get(const ::Light *la) +{ + /* Make illumination power constant */ + switch (la->type) { + case LA_AREA: { + float area = _area_size_x * _area_size_y; + float power = 1.0f / (area * 4.0f * float(M_PI)); + /* FIXME : Empirical, Fit cycles power */ + power *= 0.8f; + if (ELEM(la->area_shape, LA_AREA_DISK, LA_AREA_ELLIPSE)) { + /* Scale power to account for the lower area of the ellipse compared to the surrounding + * rectangle. */ + power *= 4.0f / M_PI; + } + return power; + } + case LA_SPOT: + case LA_LOCAL: { + return 1.0f / (4.0f * square_f(_radius) * float(M_PI * M_PI)); + } + default: + case LA_SUN: { + float power = 1.0f / (square_f(_radius) * float(M_PI)); + /* Make illumination power closer to cycles for bigger radii. Cycles uses a cos^3 term that + * we cannot reproduce so we account for that by scaling the light power. This function is + * the result of a rough manual fitting. */ + /* Simplification of: power *= 1 + r²/2 */ + power += 1.0f / (2.0f * M_PI); + + return power; + } + } +} + +float Light::point_power_get(const ::Light *la) +{ + /* Volume light is evaluated as point lights. Remove the shape power. */ + switch (la->type) { + case LA_AREA: { + /* Match cycles. Empirical fit... must correspond to some constant. */ + float power = 0.0792f * M_PI; + + /* This corrects for area light most representative point trick. The fit was found by + * reducing the average error compared to cycles. */ + float area = _area_size_x * _area_size_y; + float tmp = M_PI_2 / (M_PI_2 + sqrtf(area)); + /* Lerp between 1.0 and the limit (1 / pi). */ + power *= tmp + (1.0f - tmp) * M_1_PI; + + return power; + } + case LA_SPOT: + case LA_LOCAL: { + /* Match cycles. Empirical fit... must correspond to some constant. */ + return 0.0792f; + } + default: + case LA_SUN: { + return 1.0f; + } + } +} + +void Light::debug_draw() +{ +#ifdef DEBUG + drw_debug_sphere(_position, influence_radius_max, float4(0.8f, 0.3f, 0.0f, 1.0f)); +#endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LightModule + * \{ */ + +void LightModule::begin_sync() +{ + use_scene_lights_ = inst_.use_scene_lights(); + + /* In begin_sync so it can be animated. */ + if (assign_if_different(light_threshold_, max_ff(1e-16f, inst_.scene->eevee.light_threshold))) { + inst_.sampling.reset(); + } + + sun_lights_len_ = 0; + local_lights_len_ = 0; +} + +void LightModule::sync_light(const Object *ob, ObjectHandle &handle) +{ + if (use_scene_lights_ == false) { + return; + } + Light &light = light_map_.lookup_or_add_default(handle.object_key); + light.used = true; + if (handle.recalc != 0 || !light.initialized) { + light.sync(/* inst_.shadows, */ ob, light_threshold_); + } + sun_lights_len_ += int(light.type == LIGHT_SUN); + local_lights_len_ += int(light.type != LIGHT_SUN); +} + +void LightModule::end_sync() +{ + // ShadowModule &shadows = inst_.shadows; + + /* NOTE: We resize this buffer before removing deleted lights. */ + int lights_allocated = ceil_to_multiple_u(max_ii(light_map_.size(), 1), LIGHT_CHUNK); + light_buf_.resize(lights_allocated); + + /* Track light deletion. */ + Vector<ObjectKey, 0> deleted_keys; + /* Indices inside GPU data array. */ + int sun_lights_idx = 0; + int local_lights_idx = sun_lights_len_; + + /* Fill GPU data with scene data. */ + for (auto item : light_map_.items()) { + Light &light = item.value; + + if (!light.used) { + /* Deleted light. */ + deleted_keys.append(item.key); + // light.shadow_discard_safe(shadows); + continue; + } + + int dst_idx = (light.type == LIGHT_SUN) ? sun_lights_idx++ : local_lights_idx++; + /* Put all light data into global data SSBO. */ + light_buf_[dst_idx] = light; + +#if 0 + if (light.shadow_id != LIGHT_NO_SHADOW) { + if (light.type == LIGHT_SUN) { + light_buf_[dst_idx].shadow_data = shadows.directionals[light.shadow_id]; + } + else { + light_buf_[dst_idx].shadow_data = shadows.punctuals[light.shadow_id]; + } + } +#endif + /* Untag for next sync. */ + light.used = false; + } + /* This scene data buffer is then immutable after this point. */ + light_buf_.push_update(); + + for (auto key : deleted_keys) { + light_map_.remove(key); + } + + /* Update sampling on deletion or un-hidding (use_scene_lights). */ + if (assign_if_different(light_map_size_, light_map_.size())) { + inst_.sampling.reset(); + } + + /* If exceeding the limit, just trim off the excess to avoid glitchy rendering. */ + if (sun_lights_len_ + local_lights_len_ > CULLING_MAX_ITEM) { + sun_lights_len_ = min_ii(sun_lights_len_, CULLING_MAX_ITEM); + local_lights_len_ = min_ii(local_lights_len_, CULLING_MAX_ITEM - sun_lights_len_); + inst_.info = "Error: Too many lights in the scene."; + } + lights_len_ = sun_lights_len_ + local_lights_len_; + + /* Resize to the actual number of lights after pruning. */ + lights_allocated = ceil_to_multiple_u(max_ii(lights_len_, 1), LIGHT_CHUNK); + culling_key_buf_.resize(lights_allocated); + culling_zdist_buf_.resize(lights_allocated); + culling_light_buf_.resize(lights_allocated); + + { + /* Compute tile size and total word count. */ + uint word_per_tile = divide_ceil_u(max_ii(lights_len_, 1), 32); + int2 render_extent = inst_.film.render_extent_get(); + int2 tiles_extent; + /* Default to 32 as this is likely to be the maximum + * tile size used by hardware or compute shading. */ + uint tile_size = 16; + do { + tile_size *= 2; + tiles_extent = math::divide_ceil(render_extent, int2(tile_size)); + uint tile_count = tiles_extent.x * tiles_extent.y; + if (tile_count > max_tile_count_threshold) { + continue; + } + total_word_count_ = tile_count * word_per_tile; + + } while (total_word_count_ > max_word_count_threshold); + /* Keep aligned with storage buffer requirements. */ + total_word_count_ = ceil_to_multiple_u(total_word_count_, 32); + + culling_data_buf_.tile_word_len = word_per_tile; + culling_data_buf_.tile_size = tile_size; + culling_data_buf_.tile_x_len = tiles_extent.x; + culling_data_buf_.tile_y_len = tiles_extent.y; + culling_data_buf_.items_count = lights_len_; + culling_data_buf_.local_lights_len = local_lights_len_; + culling_data_buf_.sun_lights_len = sun_lights_len_; + } + culling_tile_buf_.resize(total_word_count_); + + culling_pass_sync(); + debug_pass_sync(); +} + +void LightModule::culling_pass_sync() +{ + uint safe_lights_len = max_ii(lights_len_, 1); + uint culling_select_dispatch_size = divide_ceil_u(safe_lights_len, CULLING_SELECT_GROUP_SIZE); + uint culling_sort_dispatch_size = divide_ceil_u(safe_lights_len, CULLING_SORT_GROUP_SIZE); + uint culling_tile_dispatch_size = divide_ceil_u(total_word_count_, CULLING_TILE_GROUP_SIZE); + + /* NOTE: We reference the buffers that may be resized or updated later. */ + { + DRW_PASS_CREATE(culling_select_ps_, DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_SELECT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_select_ps_); + DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); + DRW_shgroup_storage_block(grp, "in_light_buf", light_buf_); + DRW_shgroup_storage_block(grp, "out_light_buf", culling_light_buf_); + DRW_shgroup_storage_block(grp, "out_zdist_buf", culling_zdist_buf_); + DRW_shgroup_storage_block(grp, "out_key_buf", culling_key_buf_); + DRW_shgroup_call_compute(grp, culling_select_dispatch_size, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + } + { + DRW_PASS_CREATE(culling_sort_ps_, DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_SORT); + DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_sort_ps_); + DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); + DRW_shgroup_storage_block(grp, "in_light_buf", light_buf_); + DRW_shgroup_storage_block(grp, "out_light_buf", culling_light_buf_); + DRW_shgroup_storage_block(grp, "in_zdist_buf", culling_zdist_buf_); + DRW_shgroup_storage_block(grp, "in_key_buf", culling_key_buf_); + DRW_shgroup_call_compute(grp, culling_sort_dispatch_size, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + } + { + DRW_PASS_CREATE(culling_zbin_ps_, DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_ZBIN); + DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_zbin_ps_); + DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); + DRW_shgroup_storage_block(grp, "light_buf", culling_light_buf_); + DRW_shgroup_storage_block(grp, "out_zbin_buf", culling_zbin_buf_); + DRW_shgroup_call_compute(grp, 1, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + } + { + DRW_PASS_CREATE(culling_tile_ps_, DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_TILE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, culling_tile_ps_); + DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); + DRW_shgroup_storage_block(grp, "light_buf", culling_light_buf_); + DRW_shgroup_storage_block(grp, "out_light_tile_buf", culling_tile_buf_); + DRW_shgroup_call_compute(grp, culling_tile_dispatch_size, 1, 1); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + } +} + +void LightModule::debug_pass_sync() +{ + if (inst_.debug_mode != eDebugMode::DEBUG_LIGHT_CULLING) { + debug_draw_ps_ = nullptr; + return; + } + + debug_draw_ps_ = DRW_pass_create("LightCulling.Debug", DRW_STATE_WRITE_COLOR); + GPUShader *sh = inst_.shaders.static_shader_get(LIGHT_CULLING_DEBUG); + DRWShadingGroup *grp = DRW_shgroup_create(sh, debug_draw_ps_); + DRW_shgroup_storage_block_ref(grp, "light_buf", &culling_light_buf_); + DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); + DRW_shgroup_storage_block_ref(grp, "light_zbin_buf", &culling_zbin_buf_); + DRW_shgroup_storage_block_ref(grp, "light_tile_buf", &culling_tile_buf_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &inst_.render_buffers.depth_tx); + DRW_shgroup_call_procedural_triangles(grp, nullptr, 1); +} + +void LightModule::set_view(const DRWView *view, const int2 extent) +{ + float far_z = DRW_view_far_distance_get(view); + float near_z = DRW_view_near_distance_get(view); + + culling_data_buf_.zbin_scale = -CULLING_ZBIN_COUNT / fabsf(far_z - near_z); + culling_data_buf_.zbin_bias = -near_z * culling_data_buf_.zbin_scale; + culling_data_buf_.tile_to_uv_fac = (culling_data_buf_.tile_size / float2(extent)); + culling_data_buf_.visible_count = 0; + culling_data_buf_.push_update(); + + DRW_stats_group_start("Light Culling"); + + DRW_view_set_active(view); + DRW_draw_pass(culling_select_ps_); + DRW_draw_pass(culling_sort_ps_); + DRW_draw_pass(culling_zbin_ps_); + DRW_draw_pass(culling_tile_ps_); + + DRW_stats_group_end(); +} + +void LightModule::debug_draw(GPUFrameBuffer *view_fb) +{ + if (debug_draw_ps_ == nullptr) { + return; + } + GPU_framebuffer_bind(view_fb); + DRW_draw_pass(debug_draw_ps_); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_light.hh b/source/blender/draw/engines/eevee_next/eevee_light.hh new file mode 100644 index 00000000000..aad798ccec2 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_light.hh @@ -0,0 +1,164 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * The light module manages light data buffers and light culling system. + * + * The culling follows the principles of Tiled Culling + Z binning from: + * "Improved Culling for Tiled and Clustered Rendering" + * by Michal Drobot + * http://advances.realtimerendering.com/s2017/2017_Sig_Improved_Culling_final.pdf + * + * The culling is separated in 4 compute phases: + * - View Culling (select pass): Create a z distance and a index buffer of visible lights. + * - Light sorting: Outputs visible lights sorted by Z distance. + * - Z binning: Compute the Z bins min/max light indices. + * - Tile intersection: Fine grained 2D culling of each lights outputting a bitmap per tile. + */ + +#pragma once + +#include "BLI_bitmap.h" +#include "BLI_vector.hh" +#include "DNA_light_types.h" + +#include "eevee_camera.hh" +#include "eevee_sampling.hh" +#include "eevee_shader.hh" +#include "eevee_shader_shared.hh" +#include "eevee_sync.hh" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Light Object + * \{ */ + +struct Light : public LightData { + public: + bool initialized = false; + bool used = false; + + public: + Light() + { + shadow_id = LIGHT_NO_SHADOW; + } + + void sync(/* ShadowModule &shadows, */ const Object *ob, float threshold); + + // void shadow_discard_safe(ShadowModule &shadows); + + void debug_draw(); + + private: + float attenuation_radius_get(const ::Light *la, float light_threshold, float light_power); + void shape_parameters_set(const ::Light *la, const float scale[3]); + float shape_power_get(const ::Light *la); + float point_power_get(const ::Light *la); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name LightModule + * \{ */ + +/** + * The light module manages light data buffers and light culling system. + */ +class LightModule { + // friend ShadowModule; + + private: + /* Keep tile count reasonable for memory usage and 2D culling performance. */ + static constexpr uint max_memory_threshold = 32 * 1024 * 1024; /* 32 MiB */ + static constexpr uint max_word_count_threshold = max_memory_threshold / sizeof(uint); + static constexpr uint max_tile_count_threshold = 8192; + + Instance &inst_; + + /** Map of light objects data. Converted to flat array each frame. */ + Map<ObjectKey, Light> light_map_; + /** Flat array sent to GPU, populated from light_map_. Source buffer for light culling. */ + LightDataBuf light_buf_ = {"Lights_no_cull"}; + /** Recorded size of light_map_ (after pruning) to detect deletion. */ + int64_t light_map_size_ = 0; + /** Luminous intensity to consider the light boundary at. Used for culling. */ + float light_threshold_ = 0.01f; + /** If false, will prevent all scene light from being synced. */ + bool use_scene_lights_ = false; + /** Number of sun lights synced during the last sync. Used as offset. */ + int sun_lights_len_ = 0; + int local_lights_len_ = 0; + /** Sun plus local lights count for convenience. */ + int lights_len_ = 0; + + /** + * Light Culling + */ + + /** LightData buffer used for rendering. Filled by the culling pass. */ + LightDataBuf culling_light_buf_ = {"Lights_culled"}; + /** Culling infos. */ + LightCullingDataBuf culling_data_buf_ = {"LightCull_data"}; + /** Z-distance matching the key for each visible lights. Used for sorting. */ + LightCullingZdistBuf culling_zdist_buf_ = {"LightCull_zdist"}; + /** Key buffer containing only visible lights indices. Used for sorting. */ + LightCullingKeyBuf culling_key_buf_ = {"LightCull_key"}; + /** Zbins containing min and max light index for each Z bin. */ + LightCullingZbinBuf culling_zbin_buf_ = {"LightCull_zbin"}; + /** Bitmap of lights touching each tiles. */ + LightCullingTileBuf culling_tile_buf_ = {"LightCull_tile"}; + /** Culling compute passes. */ + DRWPass *culling_select_ps_ = nullptr; + DRWPass *culling_sort_ps_ = nullptr; + DRWPass *culling_zbin_ps_ = nullptr; + DRWPass *culling_tile_ps_ = nullptr; + /** Total number of words the tile buffer needs to contain for the render resolution. */ + uint total_word_count_ = 0; + + /** Debug Culling visualization. */ + DRWPass *debug_draw_ps_ = nullptr; + /* GPUTexture *input_depth_tx_ = nullptr; */ + + public: + LightModule(Instance &inst) : inst_(inst){}; + ~LightModule(){}; + + void begin_sync(); + void sync_light(const Object *ob, ObjectHandle &handle); + void end_sync(); + + /** + * Update acceleration structure for the given view. + */ + void set_view(const DRWView *view, const int2 extent); + + void debug_draw(GPUFrameBuffer *view_fb); + + void bind_resources(DRWShadingGroup *grp) + { + DRW_shgroup_storage_block_ref(grp, "light_buf", &culling_light_buf_); + DRW_shgroup_storage_block_ref(grp, "light_cull_buf", &culling_data_buf_); + DRW_shgroup_storage_block_ref(grp, "light_zbin_buf", &culling_zbin_buf_); + DRW_shgroup_storage_block_ref(grp, "light_tile_buf", &culling_tile_buf_); +#if 0 + DRW_shgroup_uniform_texture(grp, "shadow_atlas_tx", inst_.shadows.atlas_tx_get()); + DRW_shgroup_uniform_texture(grp, "shadow_tilemaps_tx", inst_.shadows.tilemap_tx_get()); +#endif + } + + private: + void culling_pass_sync(); + void debug_pass_sync(); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc b/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc new file mode 100644 index 00000000000..660eb9f1e22 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_motion_blur.cc @@ -0,0 +1,262 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 Blender Foundation. + */ + +/** \file + * \ingroup eevee + */ + +// #include "BLI_map.hh" +#include "DEG_depsgraph_query.h" + +#include "eevee_instance.hh" +#include "eevee_motion_blur.hh" +// #include "eevee_sampling.hh" +// #include "eevee_shader_shared.hh" +// #include "eevee_velocity.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name MotionBlurModule + * + * \{ */ + +void MotionBlurModule::init() +{ + const Scene *scene = inst_.scene; + + enabled_ = (scene->eevee.flag & SCE_EEVEE_MOTION_BLUR_ENABLED) != 0; + + if (!enabled_) { + motion_blur_fx_enabled_ = false; + return; + } + + /* Take into account the steps needed for fx motion blur. */ + int steps_count = max_ii(1, scene->eevee.motion_blur_steps) * 2 + 1; + + time_steps_.resize(steps_count); + + initial_frame_ = scene->r.cfra; + initial_subframe_ = scene->r.subframe; + frame_time_ = initial_frame_ + initial_subframe_; + shutter_position_ = scene->eevee.motion_blur_position; + shutter_time_ = scene->eevee.motion_blur_shutter; + + data_.depth_scale = scene->eevee.motion_blur_depth_scale; + motion_blur_fx_enabled_ = true; /* TODO(fclem): UI option. */ + + /* Viewport stops here. We only do Post-FX motion blur. */ + if (inst_.is_viewport()) { + enabled_ = false; + return; + } + + /* Without this there is the possibility of the curve table not being allocated. */ + BKE_curvemapping_changed((struct CurveMapping *)&scene->r.mblur_shutter_curve, false); + + Vector<float> cdf(CM_TABLE); + Sampling::cdf_from_curvemapping(scene->r.mblur_shutter_curve, cdf); + Sampling::cdf_invert(cdf, time_steps_); + + for (float &time : time_steps_) { + time = this->shutter_time_to_scene_time(time); + } + + step_id_ = 1; + + if (motion_blur_fx_enabled_) { + /* A bit weird but we have to sync the first 2 steps here because the step() + * function is only called after rendering a sample. */ + inst_.velocity.step_sync(STEP_PREVIOUS, time_steps_[0]); + inst_.velocity.step_sync(STEP_NEXT, time_steps_[2]); + } + inst_.set_time(time_steps_[1]); +} + +/* Runs after rendering a sample. */ +void MotionBlurModule::step() +{ + if (!enabled_) { + return; + } + + if (inst_.sampling.finished()) { + /* Restore original frame number. This is because the render pipeline expects it. */ + RE_engine_frame_set(inst_.render, initial_frame_, initial_subframe_); + } + else if (inst_.sampling.do_render_sync()) { + /* Time to change motion step. */ + BLI_assert(time_steps_.size() > step_id_ + 2); + step_id_ += 2; + + if (motion_blur_fx_enabled_) { + inst_.velocity.step_swap(); + inst_.velocity.step_sync(eVelocityStep::STEP_NEXT, time_steps_[step_id_ + 1]); + } + inst_.set_time(time_steps_[step_id_]); + } +} + +float MotionBlurModule::shutter_time_to_scene_time(float time) +{ + switch (shutter_position_) { + case SCE_EEVEE_MB_START: + /* No offset. */ + break; + case SCE_EEVEE_MB_CENTER: + time -= 0.5f; + break; + case SCE_EEVEE_MB_END: + time -= 1.0; + break; + default: + BLI_assert(!"Invalid motion blur position enum!"); + break; + } + time *= shutter_time_; + time += frame_time_; + return time; +} + +void MotionBlurModule::sync() +{ + /* Disable motion blur in viewport when changing camera projection type. + * Avoids really high velocities. */ + if (inst_.velocity.camera_changed_projection()) { + motion_blur_fx_enabled_ = false; + } + + if (!motion_blur_fx_enabled_) { + return; + } + + eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; + RenderBuffers &render_buffers = inst_.render_buffers; + + { + /* Create max velocity tiles. */ + DRW_PASS_CREATE(tiles_flatten_ps_, DRW_STATE_NO_DRAW); + eShaderType shader = (inst_.is_viewport()) ? MOTION_BLUR_TILE_FLATTEN_VIEWPORT : + MOTION_BLUR_TILE_FLATTEN_RENDER; + GPUShader *sh = inst_.shaders.static_shader_get(shader); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_flatten_ps_); + inst_.velocity.bind_resources(grp); + DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_); + DRW_shgroup_uniform_texture_ref(grp, "depth_tx", &render_buffers.depth_tx); + DRW_shgroup_uniform_image_ref(grp, "velocity_img", &render_buffers.vector_tx); + DRW_shgroup_uniform_image_ref(grp, "out_tiles_img", &tiles_tx_); + + DRW_shgroup_call_compute_ref(grp, dispatch_flatten_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_IMAGE_ACCESS | GPU_BARRIER_TEXTURE_FETCH); + } + { + /* Expand max velocity tiles by spreading them in their neighborhood. */ + DRW_PASS_CREATE(tiles_dilate_ps_, DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_TILE_DILATE); + DRWShadingGroup *grp = DRW_shgroup_create(sh, tiles_dilate_ps_); + DRW_shgroup_storage_block(grp, "tile_indirection_buf", tile_indirection_buf_); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_img", &tiles_tx_); + + DRW_shgroup_call_compute_ref(grp, dispatch_dilate_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_SHADER_STORAGE); + } + { + /* Do the motion blur gather algorithm. */ + DRW_PASS_CREATE(gather_ps_, DRW_STATE_NO_DRAW); + GPUShader *sh = inst_.shaders.static_shader_get(MOTION_BLUR_GATHER); + DRWShadingGroup *grp = DRW_shgroup_create(sh, gather_ps_); + inst_.sampling.bind_resources(grp); + DRW_shgroup_uniform_block(grp, "motion_blur_buf", data_); + DRW_shgroup_storage_block(grp, "tile_indirection_buf", tile_indirection_buf_); + DRW_shgroup_uniform_texture_ref_ex(grp, "depth_tx", &render_buffers.depth_tx, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "velocity_tx", &render_buffers.vector_tx, no_filter); + DRW_shgroup_uniform_texture_ref_ex(grp, "in_color_tx", &input_color_tx_, no_filter); + DRW_shgroup_uniform_image_ref(grp, "in_tiles_img", &tiles_tx_); + DRW_shgroup_uniform_image_ref(grp, "out_color_img", &output_color_tx_); + + DRW_shgroup_call_compute_ref(grp, dispatch_gather_size_); + DRW_shgroup_barrier(grp, GPU_BARRIER_TEXTURE_FETCH); + } +} + +void MotionBlurModule::render(GPUTexture **input_tx, GPUTexture **output_tx) +{ + if (!motion_blur_fx_enabled_) { + return; + } + + const Texture &depth_tx = inst_.render_buffers.depth_tx; + + int2 extent = {depth_tx.width(), depth_tx.height()}; + int2 tiles_extent = math::divide_ceil(extent, int2(MOTION_BLUR_TILE_SIZE)); + + if (inst_.is_viewport()) { + float frame_delta = fabsf(inst_.velocity.step_time_delta_get(STEP_PREVIOUS, STEP_CURRENT)); + /* Avoid highly disturbing blurs, during navigation with high shutter time. */ + if (frame_delta > 0.0f && !DRW_state_is_navigating()) { + /* Rescale motion blur intensity to be shutter time relative and avoid long streak when we + * have frame skipping. Always try to stick to what the render frame would look like. */ + data_.motion_scale = float2(shutter_time_ / frame_delta); + } + else { + /* There is no time change. Motion only comes from viewport navigation and object transform. + * Apply motion blur as smoothing and only blur towards last frame. */ + data_.motion_scale = float2(1.0f, 0.0f); + + if (was_navigating_ != DRW_state_is_navigating()) { + /* Special case for navigation events that only last for one frame (for instance mouse + * scroll for zooming). For this case we have to wait for the next frame before enabling + * the navigation motion blur. */ + was_navigating_ = DRW_state_is_navigating(); + return; + } + } + was_navigating_ = DRW_state_is_navigating(); + + /* Change texture swizzling to avoid complexity in gather pass shader. */ + GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgrg"); + } + else { + data_.motion_scale = float2(1.0f); + } + /* Second motion vector is stored inverted. */ + data_.motion_scale.y = -data_.motion_scale.y; + data_.target_size_inv = 1.0f / float2(extent); + data_.push_update(); + + input_color_tx_ = *input_tx; + output_color_tx_ = *output_tx; + + dispatch_flatten_size_ = int3(tiles_extent, 1); + dispatch_dilate_size_ = int3(math::divide_ceil(tiles_extent, int2(MOTION_BLUR_GROUP_SIZE)), 1); + dispatch_gather_size_ = int3(math::divide_ceil(extent, int2(MOTION_BLUR_GROUP_SIZE)), 1); + + DRW_stats_group_start("Motion Blur"); + + tiles_tx_.acquire(tiles_extent, GPU_RGBA16F); + + GPU_storagebuf_clear_to_zero(tile_indirection_buf_); + + DRW_draw_pass(tiles_flatten_ps_); + DRW_draw_pass(tiles_dilate_ps_); + DRW_draw_pass(gather_ps_); + + tiles_tx_.release(); + + DRW_stats_group_end(); + + if (inst_.is_viewport()) { + /* Reset swizzle since this texture might be reused in other places. */ + GPU_texture_swizzle_set(inst_.render_buffers.vector_tx, "rgba"); + } + + /* Swap buffers so that next effect has the right input. */ + *input_tx = output_color_tx_; + *output_tx = input_color_tx_; +} + +/** \} */ + +} // namespace blender::eevee
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh b/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh new file mode 100644 index 00000000000..310e94a702b --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_motion_blur.hh @@ -0,0 +1,132 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. + */ + +/** \file + * \ingroup eevee + * + * Motion blur is done by accumulating scene samples over shutter time. + * Since the number of step is discrete, quite low, and not per pixel randomized, + * we couple this with a post processing motion blur. + * + * The post-fx motion blur is done in two directions, from the previous step and to the next. + * + * For a scene with 3 motion steps, a flat shutter curve and shutter time of 2 frame + * centered on frame we have: + * + * |--------------------|--------------------| + * -1 0 1 Frames + * + * |-------------|-------------|-------------| + * 1 2 3 Motion steps + * + * |------|------|------|------|------|------| + * 0 1 2 4 5 6 7 Time Steps + * + * |-------------| One motion step blurs this range. + * -1 | +1 Objects and geometry steps are recorded here. + * 0 Scene is rendered here. + * + * Since motion step N and N+1 share one time step we reuse it to avoid an extra scene evaluation. + * + * Note that we have to evaluate -1 and +1 time steps before rendering so eval order is -1, +1, 0. + * This is because all GPUBatches from the DRWCache are being free when changing a frame. + * + * For viewport, we only have the current and previous step data to work with. So we center the + * blur on the current frame and extrapolate the motion. + * + * The Post-FX motion blur is based on: + * "A Fast and Stable Feature-Aware Motion Blur Filter" + * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai + */ + +#pragma once + +#include "BLI_map.hh" +#include "DEG_depsgraph_query.h" + +#include "eevee_sampling.hh" +#include "eevee_shader_shared.hh" +#include "eevee_velocity.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name MotionBlur + * + * \{ */ + +/** + * Manages time-steps evaluations and accumulation Motion blur. + * Also handles Post process motion blur. + */ +class MotionBlurModule { + private: + Instance &inst_; + + /** + * Array containing all steps (in scene time) we need to evaluate (not render). + * Only odd steps are rendered. The even ones are evaluated for fx motion blur. + */ + Vector<float> time_steps_; + + /** Copy of input frame and sub-frame to restore after render. */ + int initial_frame_; + float initial_subframe_; + /** Time of the frame we are rendering. */ + float frame_time_; + /** Enum controlling when the shutter opens. See SceneEEVEE.motion_blur_position. */ + int shutter_position_; + /** Time in scene frame the shutter is open. Controls the amount of blur. */ + float shutter_time_; + + /** True if motion blur is enabled as a module. */ + bool enabled_ = false; + /** True if motion blur post-fx is enabled. */ + float motion_blur_fx_enabled_ = false; + /** True if last viewport redraw state was already in navigation state. */ + bool was_navigating_ = false; + + int step_id_ = 0; + + /** Velocity tiles used to guide and speedup the gather pass. */ + TextureFromPool tiles_tx_; + + GPUTexture *input_color_tx_ = nullptr; + GPUTexture *output_color_tx_ = nullptr; + + DRWPass *tiles_flatten_ps_ = nullptr; + DRWPass *tiles_dilate_ps_ = nullptr; + DRWPass *gather_ps_ = nullptr; + + MotionBlurTileIndirectionBuf tile_indirection_buf_; + MotionBlurDataBuf data_; + /** Dispatch size for full-screen passes. */ + int3 dispatch_flatten_size_ = int3(0); + int3 dispatch_dilate_size_ = int3(0); + int3 dispatch_gather_size_ = int3(0); + + public: + MotionBlurModule(Instance &inst) : inst_(inst){}; + ~MotionBlurModule(){}; + + void init(); + + void step(); + + void sync(); + + bool postfx_enabled() const + { + return motion_blur_fx_enabled_; + } + + void render(GPUTexture **input_tx, GPUTexture **output_tx); + + private: + float shutter_time_to_scene_time(float time); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 214fe9c7153..9185ce7904a 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -101,12 +101,14 @@ DRWShadingGroup *ForwardPipeline::material_opaque_add(::Material *blender_mat, G { RenderBuffers &rbufs = inst_.render_buffers; DRWPass *pass = (blender_mat->blend_flag & MA_BL_CULL_BACKFACE) ? opaque_culled_ps_ : opaque_ps_; - // LightModule &lights = inst_.lights; + LightModule &lights = inst_.lights; + Sampling &sampling = inst_.sampling; // LightProbeModule &lightprobes = inst_.lightprobes; // RaytracingModule &raytracing = inst_.raytracing; // eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT; DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, pass); - // lights.shgroup_resources(grp); + lights.bind_resources(grp); + sampling.bind_resources(grp); // DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get()); // DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get()); // DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get()); @@ -162,19 +164,22 @@ DRWShadingGroup *ForwardPipeline::prepass_opaque_add(::Material *blender_mat, DRWShadingGroup *ForwardPipeline::material_transparent_add(::Material *blender_mat, GPUMaterial *gpumat) { - // LightModule &lights = inst_.lights; + RenderBuffers &rbufs = inst_.render_buffers; + LightModule &lights = inst_.lights; + Sampling &sampling = inst_.sampling; // LightProbeModule &lightprobes = inst_.lightprobes; // RaytracingModule &raytracing = inst_.raytracing; // eGPUSamplerState no_interp = GPU_SAMPLER_DEFAULT; DRWShadingGroup *grp = DRW_shgroup_material_create(gpumat, transparent_ps_); - // lights.shgroup_resources(grp); + lights.bind_resources(grp); + sampling.bind_resources(grp); // DRW_shgroup_uniform_block(grp, "sampling_buf", inst_.sampling.ubo_get()); // DRW_shgroup_uniform_block(grp, "grids_buf", lightprobes.grid_ubo_get()); // DRW_shgroup_uniform_block(grp, "cubes_buf", lightprobes.cube_ubo_get()); // DRW_shgroup_uniform_block(grp, "probes_buf", lightprobes.info_ubo_get()); // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_grid_tx", lightprobes.grid_tx_ref_get()); // DRW_shgroup_uniform_texture_ref(grp, "lightprobe_cube_tx", lightprobes.cube_tx_ref_get()); - // DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx); + DRW_shgroup_uniform_texture(grp, "utility_tx", inst_.pipelines.utility_tx); /* TODO(fclem): Make this only needed if material uses it ... somehow. */ // if (true) { // DRW_shgroup_uniform_texture_ref( @@ -191,6 +196,22 @@ DRWShadingGroup *ForwardPipeline::material_transparent_add(::Material *blender_m // DRW_shgroup_uniform_block(grp, "hiz_buf", inst_.hiz.ubo_get()); // DRW_shgroup_uniform_texture_ref(grp, "hiz_tx", inst_.hiz_front.texture_ref_get()); // } + { + /* TODO(fclem): This is not needed. This is only to please the OpenGL debug Layer. + * If we are to introduce transparency render-passes support, it would be through a separate + * pass. */ + /* AOVs. */ + DRW_shgroup_uniform_image_ref(grp, "aov_color_img", &rbufs.aov_color_tx); + DRW_shgroup_uniform_image_ref(grp, "aov_value_img", &rbufs.aov_value_tx); + DRW_shgroup_storage_block_ref(grp, "aov_buf", &inst_.film.aovs_info); + /* RenderPasses. */ + DRW_shgroup_uniform_image_ref(grp, "rp_normal_img", &rbufs.normal_tx); + DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_light_img", &rbufs.diffuse_light_tx); + DRW_shgroup_uniform_image_ref(grp, "rp_diffuse_color_img", &rbufs.diffuse_color_tx); + DRW_shgroup_uniform_image_ref(grp, "rp_specular_light_img", &rbufs.specular_light_tx); + DRW_shgroup_uniform_image_ref(grp, "rp_specular_color_img", &rbufs.specular_color_tx); + DRW_shgroup_uniform_image_ref(grp, "rp_emission_img", &rbufs.emission_tx); + } DRWState state_disable = DRW_STATE_WRITE_DEPTH; DRWState state_enable = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_CUSTOM; diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc index c60054496c1..b69fde7b26c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc +++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.cc @@ -24,25 +24,7 @@ namespace blender::eevee { -void RenderBuffers::sync() -{ - depth_tx.sync(); - combined_tx.sync(); - - normal_tx.sync(); - vector_tx.sync(); - diffuse_light_tx.sync(); - diffuse_color_tx.sync(); - specular_light_tx.sync(); - specular_color_tx.sync(); - volume_light_tx.sync(); - emission_tx.sync(); - environment_tx.sync(); - shadow_tx.sync(); - ambient_occlusion_tx.sync(); -} - -void RenderBuffers::acquire(int2 extent, void *owner) +void RenderBuffers::acquire(int2 extent) { auto pass_extent = [&](eViewLayerEEVEEPassType pass_bit) -> int2 { /* Use dummy texture for disabled passes. Allows correct bindings. */ @@ -53,25 +35,26 @@ void RenderBuffers::acquire(int2 extent, void *owner) eGPUTextureFormat float_format = GPU_R16F; /* Depth and combined are always needed. */ - depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8, owner); - combined_tx.acquire(extent, color_format, owner); + depth_tx.acquire(extent, GPU_DEPTH24_STENCIL8); + combined_tx.acquire(extent, color_format); - bool do_vector_render_pass = inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR; + bool do_vector_render_pass = (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR) || + (inst_.motion_blur.postfx_enabled() && !inst_.is_viewport()); /* Only RG16F when only doing only reprojection or motion blur. */ eGPUTextureFormat vector_format = do_vector_render_pass ? GPU_RGBA16F : GPU_RG16F; /* TODO(fclem): Make vector pass allocation optional if no TAA or motion blur is needed. */ - vector_tx.acquire(extent, vector_format, owner); - - normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format, owner); - diffuse_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_LIGHT), color_format, owner); - diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format, owner); - specular_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_LIGHT), color_format, owner); - specular_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_COLOR), color_format, owner); - volume_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VOLUME_LIGHT), color_format, owner); - emission_tx.acquire(pass_extent(EEVEE_RENDER_PASS_EMIT), color_format, owner); - environment_tx.acquire(pass_extent(EEVEE_RENDER_PASS_ENVIRONMENT), color_format, owner); - shadow_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SHADOW), float_format, owner); - ambient_occlusion_tx.acquire(pass_extent(EEVEE_RENDER_PASS_AO), float_format, owner); + vector_tx.acquire(extent, vector_format); + + normal_tx.acquire(pass_extent(EEVEE_RENDER_PASS_NORMAL), color_format); + diffuse_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_LIGHT), color_format); + diffuse_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_DIFFUSE_COLOR), color_format); + specular_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_LIGHT), color_format); + specular_color_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SPECULAR_COLOR), color_format); + volume_light_tx.acquire(pass_extent(EEVEE_RENDER_PASS_VOLUME_LIGHT), color_format); + emission_tx.acquire(pass_extent(EEVEE_RENDER_PASS_EMIT), color_format); + environment_tx.acquire(pass_extent(EEVEE_RENDER_PASS_ENVIRONMENT), color_format); + shadow_tx.acquire(pass_extent(EEVEE_RENDER_PASS_SHADOW), float_format); + ambient_occlusion_tx.acquire(pass_extent(EEVEE_RENDER_PASS_AO), float_format); const AOVsInfoData &aovs = inst_.film.aovs_info; aov_color_tx.ensure_2d_array( diff --git a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh index 8c91fed2f0f..787f5604aa4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh +++ b/source/blender/draw/engines/eevee_next/eevee_renderbuffers.hh @@ -48,9 +48,8 @@ class RenderBuffers { public: RenderBuffers(Instance &inst) : inst_(inst){}; - void sync(); /* Acquires (also ensures) the render buffer before rendering to them. */ - void acquire(int2 extent, void *owner); + void acquire(int2 extent); void release(); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.cc b/source/blender/draw/engines/eevee_next/eevee_sampling.cc index 1d320c75f16..76a0e98638b 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.cc +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.cc @@ -232,7 +232,7 @@ void Sampling::cdf_from_curvemapping(const CurveMapping &curve, Vector<float> &c BLI_assert(cdf.size() > 1); cdf[0] = 0.0f; /* Actual CDF evaluation. */ - for (int u : cdf.index_range()) { + for (int u : IndexRange(cdf.size() - 1)) { float x = (float)(u + 1) / (float)(cdf.size() - 1); cdf[u + 1] = cdf[u] + BKE_curvemapping_evaluateF(&curve, 0, x); } diff --git a/source/blender/draw/engines/eevee_next/eevee_sampling.hh b/source/blender/draw/engines/eevee_next/eevee_sampling.hh index c604ecef40b..be87ee74886 100644 --- a/source/blender/draw/engines/eevee_next/eevee_sampling.hh +++ b/source/blender/draw/engines/eevee_next/eevee_sampling.hh @@ -27,11 +27,11 @@ class Sampling { Instance &inst_; /* Number of samples in the first ring of jittered depth of field. */ - constexpr static uint64_t dof_web_density_ = 6; + static constexpr uint64_t dof_web_density_ = 6; /* High number of sample for viewport infinite rendering. */ - constexpr static uint64_t infinite_sample_count_ = 0xFFFFFFu; + static constexpr uint64_t infinite_sample_count_ = 0xFFFFFFu; /* During interactive rendering, loop over the first few samples. */ - constexpr static uint64_t interactive_sample_max_ = 8; + static constexpr uint64_t interactive_sample_max_ = 8; /** 0 based current sample. Might not increase sequentially in viewport. */ uint64_t sample_ = 0; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.cc b/source/blender/draw/engines/eevee_next/eevee_shader.cc index 7db9692783a..a535d3407ac 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.cc +++ b/source/blender/draw/engines/eevee_next/eevee_shader.cc @@ -82,6 +82,58 @@ const char *ShaderModule::static_shader_create_info_name_get(eShaderType shader_ return "eevee_film_frag"; case FILM_COMP: return "eevee_film_comp"; + case MOTION_BLUR_GATHER: + return "eevee_motion_blur_gather"; + case MOTION_BLUR_TILE_DILATE: + return "eevee_motion_blur_tiles_dilate"; + case MOTION_BLUR_TILE_FLATTEN_RENDER: + return "eevee_motion_blur_tiles_flatten_render"; + case MOTION_BLUR_TILE_FLATTEN_VIEWPORT: + return "eevee_motion_blur_tiles_flatten_viewport"; + case DOF_BOKEH_LUT: + return "eevee_depth_of_field_bokeh_lut"; + case DOF_DOWNSAMPLE: + return "eevee_depth_of_field_downsample"; + case DOF_FILTER: + return "eevee_depth_of_field_filter"; + case DOF_GATHER_FOREGROUND_LUT: + return "eevee_depth_of_field_gather_foreground_lut"; + case DOF_GATHER_FOREGROUND: + return "eevee_depth_of_field_gather_foreground_no_lut"; + case DOF_GATHER_BACKGROUND_LUT: + return "eevee_depth_of_field_gather_background_lut"; + case DOF_GATHER_BACKGROUND: + return "eevee_depth_of_field_gather_background_no_lut"; + case DOF_GATHER_HOLE_FILL: + return "eevee_depth_of_field_hole_fill"; + case DOF_REDUCE: + return "eevee_depth_of_field_reduce"; + case DOF_RESOLVE: + return "eevee_depth_of_field_resolve_no_lut"; + case DOF_RESOLVE_LUT: + return "eevee_depth_of_field_resolve_lut"; + case DOF_SETUP: + return "eevee_depth_of_field_setup"; + case DOF_SCATTER: + return "eevee_depth_of_field_scatter"; + case DOF_STABILIZE: + return "eevee_depth_of_field_stabilize"; + case DOF_TILES_DILATE_MINABS: + return "eevee_depth_of_field_tiles_dilate_minabs"; + case DOF_TILES_DILATE_MINMAX: + return "eevee_depth_of_field_tiles_dilate_minmax"; + case DOF_TILES_FLATTEN: + return "eevee_depth_of_field_tiles_flatten"; + case LIGHT_CULLING_DEBUG: + return "eevee_light_culling_debug"; + case LIGHT_CULLING_SELECT: + return "eevee_light_culling_select"; + case LIGHT_CULLING_SORT: + return "eevee_light_culling_sort"; + case LIGHT_CULLING_TILE: + return "eevee_light_culling_tile"; + case LIGHT_CULLING_ZBIN: + return "eevee_light_culling_zbin"; /* To avoid compiler warning about missing case. */ case MAX_SHADER_TYPE: return ""; diff --git a/source/blender/draw/engines/eevee_next/eevee_shader.hh b/source/blender/draw/engines/eevee_next/eevee_shader.hh index 280aaab4e1c..5b43a1abf43 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader.hh @@ -29,6 +29,35 @@ enum eShaderType { FILM_FRAG = 0, FILM_COMP, + DOF_BOKEH_LUT, + DOF_DOWNSAMPLE, + DOF_FILTER, + DOF_GATHER_BACKGROUND_LUT, + DOF_GATHER_BACKGROUND, + DOF_GATHER_FOREGROUND_LUT, + DOF_GATHER_FOREGROUND, + DOF_GATHER_HOLE_FILL, + DOF_REDUCE, + DOF_RESOLVE_LUT, + DOF_RESOLVE, + DOF_SCATTER, + DOF_SETUP, + DOF_STABILIZE, + DOF_TILES_DILATE_MINABS, + DOF_TILES_DILATE_MINMAX, + DOF_TILES_FLATTEN, + + LIGHT_CULLING_DEBUG, + LIGHT_CULLING_SELECT, + LIGHT_CULLING_SORT, + LIGHT_CULLING_TILE, + LIGHT_CULLING_ZBIN, + + MOTION_BLUR_GATHER, + MOTION_BLUR_TILE_DILATE, + MOTION_BLUR_TILE_FLATTEN_RENDER, + MOTION_BLUR_TILE_FLATTEN_VIEWPORT, + MAX_SHADER_TYPE, }; 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 3c10f633740..885317fc673 100644 --- a/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh +++ b/source/blender/draw/engines/eevee_next/eevee_shader_shared.hh @@ -23,11 +23,60 @@ using draw::SwapChain; using draw::Texture; using draw::TextureFromPool; +constexpr eGPUSamplerState no_filter = GPU_SAMPLER_DEFAULT; +constexpr eGPUSamplerState with_filter = GPU_SAMPLER_FILTER; + #endif #define UBO_MIN_MAX_SUPPORTED_SIZE 1 << 14 /* -------------------------------------------------------------------- */ +/** \name Debug Mode + * \{ */ + +/** These are just to make more sense of G.debug_value's values. Reserved range is 1-30. */ +enum eDebugMode : uint32_t { + DEBUG_NONE = 0u, + /** + * Gradient showing light evaluation hotspots. + */ + DEBUG_LIGHT_CULLING = 1u, + /** + * Tilemaps to screen. Is also present in other modes. + * - Black pixels, no pages allocated. + * - Green pixels, pages cached. + * - Red pixels, pages allocated. + */ + DEBUG_SHADOW_TILEMAPS = 2u, + /** + * Random color per pages. Validates page density allocation and sampling. + */ + DEBUG_SHADOW_PAGES = 3u, + /** + * Outputs random color per tilemap (or tilemap level). Validates tilemaps coverage. + * Black means not covered by any tilemaps LOD of the shadow. + */ + DEBUG_SHADOW_LOD = 4u, + /** + * Outputs white pixels for pages allocated and black pixels for unused pages. + * This needs DEBUG_SHADOW_PAGE_ALLOCATION_ENABLED defined in order to work. + */ + DEBUG_SHADOW_PAGE_ALLOCATION = 5u, + /** + * Outputs the tilemap atlas. Default tilemap is too big for the usual screen resolution. + * Try lowering SHADOW_TILEMAP_PER_ROW and SHADOW_MAX_TILEMAP before using this option. + */ + DEBUG_SHADOW_TILE_ALLOCATION = 6u, + /** + * Visualize linear depth stored in the atlas regions of the active light. + * This way, one can check if the rendering, the copying and the shadow sampling functions works. + */ + DEBUG_SHADOW_SHADOW_DEPTH = 7u +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Sampling * \{ */ @@ -124,7 +173,7 @@ struct CameraData { float clip_far; eCameraType type; - bool initialized; + bool1 initialized; #ifdef __cplusplus /* Small constructor to allow detecting new buffers. */ @@ -312,6 +361,258 @@ BLI_STATIC_ASSERT_ALIGN(VelocityGeometryIndex, 16) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Motion Blur + * \{ */ + +#define MOTION_BLUR_TILE_SIZE 32 +#define MOTION_BLUR_MAX_TILE 512 /* 16384 / MOTION_BLUR_TILE_SIZE */ +struct MotionBlurData { + /** As the name suggests. Used to avoid a division in the sampling. */ + float2 target_size_inv; + /** Viewport motion scaling factor. Make blur relative to frame time not render time. */ + float2 motion_scale; + /** Depth scaling factor. Avoid blurring background behind moving objects. */ + float depth_scale; + + float _pad0, _pad1, _pad2; +}; +BLI_STATIC_ASSERT_ALIGN(MotionBlurData, 16) + +/* For some reasons some GLSL compilers do not like this struct. + * So we declare it as a uint array instead and do indexing ourselves. */ +#ifdef __cplusplus +struct MotionBlurTileIndirection { + /** + * Stores indirection to the tile with the highest velocity covering each tile. + * This is stored using velocity in the MSB to be able to use atomicMax operations. + */ + uint prev[MOTION_BLUR_MAX_TILE][MOTION_BLUR_MAX_TILE]; + uint next[MOTION_BLUR_MAX_TILE][MOTION_BLUR_MAX_TILE]; +}; +BLI_STATIC_ASSERT_ALIGN(MotionBlurTileIndirection, 16) +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Depth of field + * \{ */ + +/* 5% error threshold. */ +#define DOF_FAST_GATHER_COC_ERROR 0.05 +#define DOF_GATHER_RING_COUNT 5 +#define DOF_DILATE_RING_COUNT 3 + +struct DepthOfFieldData { + /** Size of the render targets for gather & scatter passes. */ + int2 extent; + /** Size of a pixel in uv space (1.0 / extent). */ + float2 texel_size; + /** Scale factor for anisotropic bokeh. */ + float2 bokeh_anisotropic_scale; + float2 bokeh_anisotropic_scale_inv; + /* Correction factor to align main target pixels with the filtered mipmap chain texture. */ + float2 gather_uv_fac; + /** Scatter parameters. */ + float scatter_coc_threshold; + float scatter_color_threshold; + float scatter_neighbor_max_color; + int scatter_sprite_per_row; + /** Number of side the bokeh shape has. */ + float bokeh_blades; + /** Rotation of the bokeh shape. */ + float bokeh_rotation; + /** Multiplier and bias to apply to linear depth to Circle of confusion (CoC). */ + float coc_mul, coc_bias; + /** Maximum absolute allowed Circle of confusion (CoC). Min of computed max and user max. */ + float coc_abs_max; + /** Copy of camera type. */ + eCameraType camera_type; + /** Weights of spatial filtering in stabilize pass. Not array to avoid alignment restriction. */ + float4 filter_samples_weight; + float filter_center_weight; + /** Max number of sprite in the scatter pass for each ground. */ + int scatter_max_rect; + + int _pad0, _pad1; +}; +BLI_STATIC_ASSERT_ALIGN(DepthOfFieldData, 16) + +struct ScatterRect { + /** Color and CoC of the 4 pixels the scatter sprite represents. */ + float4 color_and_coc[4]; + /** Rect center position in half pixel space. */ + float2 offset; + /** Rect half extent in half pixel space. */ + float2 half_extent; +}; +BLI_STATIC_ASSERT_ALIGN(ScatterRect, 16) + +/** WORKAROUND(@fclem): This is because this file is included before common_math_lib.glsl. */ +#ifndef M_PI +# define EEVEE_PI +# define M_PI 3.14159265358979323846 /* pi */ +#endif + +static inline float coc_radius_from_camera_depth(DepthOfFieldData dof, float depth) +{ + depth = (dof.camera_type != CAMERA_ORTHO) ? 1.0f / depth : depth; + return dof.coc_mul * depth + dof.coc_bias; +} + +static inline float regular_polygon_side_length(float sides_count) +{ + return 2.0f * sinf(M_PI / sides_count); +} + +/* Returns intersection ratio between the radius edge at theta and the regular polygon edge. + * Start first corners at theta == 0. */ +static inline float circle_to_polygon_radius(float sides_count, float theta) +{ + /* From Graphics Gems from CryENGINE 3 (Siggraph 2013) by Tiago Sousa (slide + * 36). */ + float side_angle = (2.0f * M_PI) / sides_count; + return cosf(side_angle * 0.5f) / + cosf(theta - side_angle * floorf((sides_count * theta + M_PI) / (2.0f * M_PI))); +} + +/* Remap input angle to have homogenous spacing of points along a polygon edge. + * Expects theta to be in [0..2pi] range. */ +static inline float circle_to_polygon_angle(float sides_count, float theta) +{ + float side_angle = (2.0f * M_PI) / sides_count; + float halfside_angle = side_angle * 0.5f; + float side = floorf(theta / side_angle); + /* Length of segment from center to the middle of polygon side. */ + float adjacent = circle_to_polygon_radius(sides_count, 0.0f); + + /* This is the relative position of the sample on the polygon half side. */ + float local_theta = theta - side * side_angle; + float ratio = (local_theta - halfside_angle) / halfside_angle; + + float halfside_len = regular_polygon_side_length(sides_count) * 0.5f; + float opposite = ratio * halfside_len; + + /* NOTE: atan(y_over_x) has output range [-M_PI_2..M_PI_2]. */ + float final_local_theta = atanf(opposite / adjacent); + + return side * side_angle + final_local_theta; +} + +#ifdef EEVEE_PI +# undef M_PI +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Culling + * \{ */ + +/* Number of items we can cull. Limited by how we store CullingZBin. */ +#define CULLING_MAX_ITEM 65536 +/* Fine grained subdivision in the Z direction. Limited by the LDS in z-binning compute shader. */ +#define CULLING_ZBIN_COUNT 4096 +/* Max tile map resolution per axes. */ +#define CULLING_TILE_RES 16 + +struct LightCullingData { + /** Scale applied to tile pixel coordinates to get target UV coordinate. */ + float2 tile_to_uv_fac; + /** Scale and bias applied to linear Z to get zbin. */ + float zbin_scale; + float zbin_bias; + /** Valid item count in the source data array. */ + uint items_count; + /** Items that are processed by the 2.5D culling. */ + uint local_lights_len; + /** Items that are **NOT** processed by the 2.5D culling (i.e: Sun Lights). */ + uint sun_lights_len; + /** Number of items that passes the first culling test. */ + uint visible_count; + /** Extent of one square tile in pixels. */ + float tile_size; + /** Number of tiles on the X/Y axis. */ + uint tile_x_len; + uint tile_y_len; + /** Number of word per tile. Depends on the maximum number of lights. */ + uint tile_word_len; +}; +BLI_STATIC_ASSERT_ALIGN(LightCullingData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lights + * \{ */ + +#define LIGHT_NO_SHADOW -1 + +enum eLightType : uint32_t { + LIGHT_SUN = 0u, + LIGHT_POINT = 1u, + LIGHT_SPOT = 2u, + LIGHT_RECT = 3u, + LIGHT_ELLIPSE = 4u +}; + +static inline bool is_area_light(eLightType type) +{ + return type >= LIGHT_RECT; +} + +struct LightData { + /** Normalized object matrix. Last column contains data accessible using the following macros. */ + float4x4 object_mat; + /** Packed data in the last column of the object_mat. */ +#define _area_size_x object_mat[0][3] +#define _area_size_y object_mat[1][3] +#define _radius _area_size_x +#define _spot_mul object_mat[2][3] +#define _spot_bias object_mat[3][3] + /** Aliases for axes. */ +#ifndef USE_GPU_SHADER_CREATE_INFO +# define _right object_mat[0] +# define _up object_mat[1] +# define _back object_mat[2] +# define _position object_mat[3] +#else +# define _right object_mat[0].xyz +# define _up object_mat[1].xyz +# define _back object_mat[2].xyz +# define _position object_mat[3].xyz +#endif + /** Influence radius (inverted and squared) adjusted for Surface / Volume power. */ + float influence_radius_invsqr_surface; + float influence_radius_invsqr_volume; + /** Maximum influence radius. Used for culling. */ + float influence_radius_max; + /** Index of the shadow struct on CPU. -1 means no shadow. */ + int shadow_id; + /** NOTE: It is ok to use float3 here. A float is declared right after it. + * float3 is also aligned to 16 bytes. */ + float3 color; + /** Power depending on shader type. */ + float diffuse_power; + float specular_power; + float volume_power; + float transmit_power; + /** Special radius factor for point lighting. */ + float radius_squared; + /** Light Type. */ + eLightType type; + /** Spot angle tangent. */ + float spot_tan; + /** Spot size. Aligned to size of float2. */ + float2 spot_size_inv; + /** Associated shadow data. Only valid if shadow_id is not LIGHT_NO_SHADOW. */ + // ShadowData shadow_data; +}; +BLI_STATIC_ASSERT_ALIGN(LightData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Ray-Tracing * \{ */ @@ -332,6 +633,34 @@ enum eClosureBits : uint32_t { /** \} */ /* -------------------------------------------------------------------- */ +/** \name Subsurface + * \{ */ + +#define SSS_SAMPLE_MAX 64 +#define SSS_BURLEY_TRUNCATE 16.0 +#define SSS_BURLEY_TRUNCATE_CDF 0.9963790093708328 +#define SSS_TRANSMIT_LUT_SIZE 64.0 +#define SSS_TRANSMIT_LUT_RADIUS 1.218 +#define SSS_TRANSMIT_LUT_SCALE ((SSS_TRANSMIT_LUT_SIZE - 1.0) / float(SSS_TRANSMIT_LUT_SIZE)) +#define SSS_TRANSMIT_LUT_BIAS (0.5 / float(SSS_TRANSMIT_LUT_SIZE)) +#define SSS_TRANSMIT_LUT_STEP_RES 64.0 + +struct SubsurfaceData { + /** xy: 2D sample position [-1..1], zw: sample_bounds. */ + /* NOTE(fclem) Using float4 for alignment. */ + float4 samples[SSS_SAMPLE_MAX]; + /** Sample index after which samples are not randomly rotated anymore. */ + int jitter_threshold; + /** Number of samples precomputed in the set. */ + int sample_len; + int _pad0; + int _pad1; +}; +BLI_STATIC_ASSERT_ALIGN(SubsurfaceData, 16) + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Utility Texture * \{ */ @@ -370,7 +699,18 @@ float4 utility_tx_sample(sampler2DArray util_tx, float2 uv, float layer) using AOVsInfoDataBuf = draw::StorageBuffer<AOVsInfoData>; using CameraDataBuf = draw::UniformBuffer<CameraData>; +using LightDataBuf = draw::StorageArrayBuffer<LightData, LIGHT_CHUNK>; +using LightCullingDataBuf = draw::StorageBuffer<LightCullingData>; +using LightCullingKeyBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>; +using LightCullingTileBuf = draw::StorageArrayBuffer<uint, LIGHT_CHUNK, true>; +using LightCullingZbinBuf = draw::StorageArrayBuffer<uint, CULLING_ZBIN_COUNT, true>; +using LightCullingZdistBuf = draw::StorageArrayBuffer<float, LIGHT_CHUNK, true>; +using DepthOfFieldDataBuf = draw::UniformBuffer<DepthOfFieldData>; +using DepthOfFieldScatterListBuf = draw::StorageArrayBuffer<ScatterRect, 16, true>; +using DrawIndirectBuf = draw::StorageBuffer<DrawCommand, true>; using FilmDataBuf = draw::UniformBuffer<FilmData>; +using MotionBlurDataBuf = draw::UniformBuffer<MotionBlurData>; +using MotionBlurTileIndirectionBuf = draw::StorageBuffer<MotionBlurTileIndirection, true>; using SamplingDataBuf = draw::StorageBuffer<SamplingData>; using VelocityGeometryBuf = draw::StorageArrayBuffer<float4, 16, true>; using VelocityIndexBuf = draw::StorageArrayBuffer<VelocityIndex, 16>; diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.cc b/source/blender/draw/engines/eevee_next/eevee_velocity.cc index 048daf1b2db..36734f0c28c 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.cc +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.cc @@ -32,13 +32,16 @@ namespace blender::eevee { void VelocityModule::init() { - if (inst_.render && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR)) { - /* No motion blur and the vector pass was requested. Do the step sync here. */ + if (inst_.render && (inst_.film.enabled_passes_get() & EEVEE_RENDER_PASS_VECTOR) != 0) { + /* No motion blur and the vector pass was requested. Do the steps sync here. */ const Scene *scene = inst_.scene; float initial_time = scene->r.cfra + scene->r.subframe; step_sync(STEP_PREVIOUS, initial_time - 1.0f); step_sync(STEP_NEXT, initial_time + 1.0f); + inst_.set_time(initial_time); + step_ = STEP_CURRENT; + /* Let the main sync loop handle the current step. */ } } @@ -64,10 +67,12 @@ void VelocityModule::step_camera_sync() { inst_.camera.sync(); *camera_steps[step_] = inst_.camera.data_get(); + step_time[step_] = inst_.scene->r.cfra + inst_.scene->r.subframe; /* Fix undefined camera steps when rendering is starting. */ if ((step_ == STEP_CURRENT) && (camera_steps[STEP_PREVIOUS]->initialized == false)) { *camera_steps[STEP_PREVIOUS] = *static_cast<CameraData *>(camera_steps[step_]); camera_steps[STEP_PREVIOUS]->initialized = true; + step_time[STEP_PREVIOUS] = step_time[step_]; } } @@ -212,6 +217,7 @@ void VelocityModule::step_swap() SWAP(VelocityObjectBuf *, object_steps[step_a], object_steps[step_b]); SWAP(VelocityGeometryBuf *, geometry_steps[step_a], geometry_steps[step_b]); SWAP(CameraDataBuf *, camera_steps[step_a], camera_steps[step_b]); + SWAP(float, step_time[step_a], step_time[step_b]); for (VelocityObjectData &vel : velocity_map.values()) { vel.obj.ofs[step_a] = vel.obj.ofs[step_b]; @@ -238,10 +244,7 @@ void VelocityModule::step_swap() void VelocityModule::begin_sync() { - if (inst_.is_viewport()) { - /* Viewport always evaluate current step. */ - step_ = STEP_CURRENT; - } + step_ = STEP_CURRENT; step_camera_sync(); object_steps_usage[step_] = 0; } @@ -360,6 +363,21 @@ bool VelocityModule::camera_has_motion() const *camera_steps[STEP_NEXT] != *camera_steps[STEP_CURRENT]; } +bool VelocityModule::camera_changed_projection() const +{ + /* Only valid after sync. */ + if (inst_.is_viewport()) { + return camera_steps[STEP_PREVIOUS]->type != camera_steps[STEP_CURRENT]->type; + } + /* Cannot happen in render mode since we set the type during the init phase. */ + return false; +} + +float VelocityModule::step_time_delta_get(eVelocityStep start, eVelocityStep end) const +{ + return step_time[end] - step_time[start]; +} + /** \} */ } // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_velocity.hh b/source/blender/draw/engines/eevee_next/eevee_velocity.hh index 826cd631a96..01b8a5fb8c1 100644 --- a/source/blender/draw/engines/eevee_next/eevee_velocity.hh +++ b/source/blender/draw/engines/eevee_next/eevee_velocity.hh @@ -56,6 +56,8 @@ class VelocityModule { int3 object_steps_usage = int3(0); /** Buffer of all #VelocityIndex used in this frame. Indexed by draw manager resource id. */ VelocityIndexBuf indirection_buf; + /** Frame time at which each steps were evaluated. */ + float3 step_time; /** * Copies of camera data. One for previous and one for next time step. @@ -78,7 +80,6 @@ class VelocityModule { } for (CameraDataBuf *&step_buf : camera_steps) { step_buf = new CameraDataBuf(); - /* */ } }; @@ -112,6 +113,10 @@ class VelocityModule { void bind_resources(DRWShadingGroup *grp); bool camera_has_motion() const; + bool camera_changed_projection() const; + + /* Returns frame time difference between two steps. */ + float step_time_delta_get(eVelocityStep start, eVelocityStep end) const; private: bool object_has_velocity(const Object *ob); diff --git a/source/blender/draw/engines/eevee_next/eevee_view.cc b/source/blender/draw/engines/eevee_next/eevee_view.cc index 55741bee4df..b7154465a70 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.cc +++ b/source/blender/draw/engines/eevee_next/eevee_view.cc @@ -79,14 +79,11 @@ void ShadingView::sync() render_view_ = DRW_view_create_sub(main_view_, viewmat_p, winmat_p); // dof_.sync(winmat_p, extent_); - // mb_.sync(extent_); // rt_buffer_opaque_.sync(extent_); // rt_buffer_refract_.sync(extent_); // inst_.hiz_back.view_sync(extent_); // inst_.hiz_front.view_sync(extent_); // inst_.gbuffer.view_sync(extent_); - - postfx_tx_.sync(); } void ShadingView::render() @@ -96,12 +93,8 @@ void ShadingView::render() } /* Query temp textures and create frame-buffers. */ - /* HACK: View name should be unique and static. - * With this, we can reuse the same texture across views. */ - DrawEngineType *owner = (DrawEngineType *)name_; - RenderBuffers &rbufs = inst_.render_buffers; - rbufs.acquire(extent_, owner); + rbufs.acquire(extent_); combined_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx), GPU_ATTACHMENT_TEXTURE(rbufs.combined_tx)); prepass_fb_.ensure(GPU_ATTACHMENT_TEXTURE(rbufs.depth_tx), @@ -125,6 +118,9 @@ void ShadingView::render() inst_.pipelines.world.render(); + /* TODO(fclem): Move it after the first prepass (and hiz update) once pipeline is stabilized. */ + inst_.lights.set_view(render_view_, extent_); + // inst_.pipelines.deferred.render( // render_view_, rt_buffer_opaque_, rt_buffer_refract_, depth_tx_, combined_tx_); @@ -135,12 +131,13 @@ void ShadingView::render() inst_.pipelines.forward.render( render_view_, prepass_fb_, combined_fb_, rbufs.depth_tx, rbufs.combined_tx); - // inst_.lights.debug_draw(view_fb_); - // inst_.shadows.debug_draw(view_fb_); + inst_.lights.debug_draw(combined_fb_); - // GPUTexture *final_radiance_tx = render_post(combined_tx_); + GPUTexture *combined_final_tx = render_postfx(rbufs.combined_tx); - inst_.film.accumulate(sub_view_); + inst_.film.accumulate(sub_view_, combined_final_tx); + + // inst_.shadows.debug_draw(); rbufs.release(); postfx_tx_.release(); @@ -148,23 +145,19 @@ void ShadingView::render() DRW_stats_group_end(); } -GPUTexture *ShadingView::render_post(GPUTexture *input_tx) +GPUTexture *ShadingView::render_postfx(GPUTexture *input_tx) { -#if 0 - if (!dof_.postfx_enabled() && !mb_.enabled()) { + if (!inst_.depth_of_field.postfx_enabled() && !inst_.motion_blur.postfx_enabled()) { return input_tx; } - /* HACK: View name should be unique and static. - * With this, we can reuse the same texture across views. */ - postfx_tx_.acquire(extent_, GPU_RGBA16F, (void *)name_); + postfx_tx_.acquire(extent_, GPU_RGBA16F); - GPUTexture *velocity_tx = velocity_.view_vectors_get(); GPUTexture *output_tx = postfx_tx_; /* Swapping is done internally. Actual output is set to the next input. */ - dof_.render(depth_tx_, &input_tx, &output_tx); - mb_.render(depth_tx_, velocity_tx, &input_tx, &output_tx); -#endif + inst_.depth_of_field.render(&input_tx, &output_tx, dof_buffer_); + inst_.motion_blur.render(&input_tx, &output_tx); + return input_tx; } @@ -187,13 +180,10 @@ void ShadingView::update_view() window_translate_m4(winmat.ptr(), winmat.ptr(), UNPACK2(jitter)); DRW_view_update_sub(sub_view_, viewmat.ptr(), winmat.ptr()); - /* FIXME(fclem): The offset may be is noticeably large and the culling might make object pop + /* FIXME(fclem): The offset may be noticeably large and the culling might make object pop * out of the blurring radius. To fix this, use custom enlarged culling matrix. */ - // dof_.jitter_apply(winmat, viewmat); + inst_.depth_of_field.jitter_apply(winmat, viewmat); DRW_view_update_sub(render_view_, viewmat.ptr(), winmat.ptr()); - - // inst_.lightprobes.set_view(render_view_, extent_); - // inst_.lights.set_view(render_view_, extent_, !inst_.use_scene_lights()); } /** \} */ diff --git a/source/blender/draw/engines/eevee_next/eevee_view.hh b/source/blender/draw/engines/eevee_next/eevee_view.hh index c6faebdd0e5..65f27aba795 100644 --- a/source/blender/draw/engines/eevee_next/eevee_view.hh +++ b/source/blender/draw/engines/eevee_next/eevee_view.hh @@ -41,13 +41,10 @@ class ShadingView { /** Matrix to apply to the viewmat. */ const float (*face_matrix_)[4]; - /** Post-FX modules. */ - // DepthOfField dof_; - // MotionBlur mb_; - /** Raytracing persistent buffers. Only opaque and refraction can have surface tracing. */ // RaytraceBuffer rt_buffer_opaque_; // RaytraceBuffer rt_buffer_refract_; + DepthOfFieldBuffer dof_buffer_; Framebuffer prepass_fb_; Framebuffer combined_fb_; @@ -78,7 +75,7 @@ class ShadingView { void render(); - GPUTexture *render_post(GPUTexture *input_tx); + GPUTexture *render_postfx(GPUTexture *input_tx); private: void update_view(); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl new file mode 100644 index 00000000000..d5fdaae6fc1 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_colorspace_lib.glsl @@ -0,0 +1,37 @@ + +/* -------------------------------------------------------------------- */ +/** \name YCoCg + * \{ */ + +vec3 colorspace_YCoCg_from_scene_linear(vec3 rgb_color) +{ + const mat3 colorspace_tx = transpose(mat3(vec3(1, 2, 1), /* Y */ + vec3(2, 0, -2), /* Co */ + vec3(-1, 2, -1))); /* Cg */ + return colorspace_tx * rgb_color; +} + +vec4 colorspace_YCoCg_from_scene_linear(vec4 rgba_color) +{ + return vec4(colorspace_YCoCg_from_scene_linear(rgba_color.rgb), rgba_color.a); +} + +vec3 colorspace_scene_linear_from_YCoCg(vec3 ycocg_color) +{ + float Y = ycocg_color.x; + float Co = ycocg_color.y; + float Cg = ycocg_color.z; + + vec3 rgb_color; + rgb_color.r = Y + Co - Cg; + rgb_color.g = Y + Cg; + rgb_color.b = Y - Co - Cg; + return rgb_color * 0.25; +} + +vec4 colorspace_scene_linear_from_YCoCg(vec4 ycocg_color) +{ + return vec4(colorspace_scene_linear_from_YCoCg(ycocg_color.rgb), ycocg_color.a); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl new file mode 100644 index 00000000000..99a47c541e9 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_accumulator_lib.glsl @@ -0,0 +1,680 @@ + +/** + * Depth of Field Gather accumulator. + * We currently have only 2 which are very similar. + * One is for the halfres gather passes and the other one for slight in focus regions. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Options. + * \{ */ + +/* Quality options */ +#ifdef DOF_HOLEFILL_PASS +/* No need for very high density for hole_fill. */ +const int gather_ring_count = 3; +const int gather_ring_density = 3; +const int gather_max_density_change = 0; +const int gather_density_change_ring = 1; +#else +const int gather_ring_count = DOF_GATHER_RING_COUNT; +const int gather_ring_density = 3; +const int gather_max_density_change = 50; /* Dictates the maximum good quality blur. */ +const int gather_density_change_ring = 1; +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Constants. + * \{ */ + +const float unit_ring_radius = 1.0 / float(gather_ring_count); +const float unit_sample_radius = 1.0 / float(gather_ring_count + 0.5); +const float large_kernel_radius = 0.5 + float(gather_ring_count); +const float smaller_kernel_radius = 0.5 + float(gather_ring_count - gather_density_change_ring); +/* NOTE(fclem) the bias is reducing issues with density change visible transition. */ +const float radius_downscale_factor = smaller_kernel_radius / large_kernel_radius; +const int change_density_at_ring = (gather_ring_count - gather_density_change_ring + 1); +const float coc_radius_error = 2.0; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gather common. + * \{ */ + +struct DofGatherData { + vec4 color; + float weight; + float dist; /* TODO remove */ + /* For scatter occlusion. */ + float coc; + float coc_sqr; + /* For ring bucket merging. */ + float transparency; + + float layer_opacity; +}; + +#define GATHER_DATA_INIT DofGatherData(vec4(0.0), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0) + +/* Intersection with the center of the kernel. */ +float dof_intersection_weight(float coc, float distance_from_center, float intersection_multiplier) +{ + if (no_smooth_intersection) { + return step(0.0, (abs(coc) - distance_from_center)); + } + else { + /* (Slide 64). */ + return saturate((abs(coc) - distance_from_center) * intersection_multiplier + 0.5); + } +} + +/* Returns weight of the sample for the outer bucket (containing previous + * rings). */ +float dof_gather_accum_weight(float coc, float bordering_radius, bool first_ring) +{ + /* First ring has nothing to be mixed against. */ + if (first_ring) { + return 0.0; + } + return saturate(coc - bordering_radius); +} + +void dof_gather_ammend_weight(inout DofGatherData sample_data, float weight) +{ + sample_data.color *= weight; + sample_data.coc *= weight; + sample_data.coc_sqr *= weight; + sample_data.weight *= weight; +} + +void dof_gather_accumulate_sample(DofGatherData sample_data, + float weight, + inout DofGatherData accum_data) +{ + accum_data.color += sample_data.color * weight; + accum_data.coc += sample_data.coc * weight; + accum_data.coc_sqr += sample_data.coc * (sample_data.coc * weight); + accum_data.weight += weight; +} + +void dof_gather_accumulate_sample_pair(DofGatherData pair_data[2], + float bordering_radius, + float intersection_multiplier, + bool first_ring, + const bool do_fast_gather, + const bool is_foreground, + inout DofGatherData ring_data, + inout DofGatherData accum_data) +{ + if (do_fast_gather) { + for (int i = 0; i < 2; i++) { + dof_gather_accumulate_sample(pair_data[i], 1.0, accum_data); + accum_data.layer_opacity += 1.0; + } + return; + } + +#if 0 + const float mirroring_threshold = -dof_layer_threshold - dof_layer_offset; + /* TODO(fclem) Promote to parameter? dither with Noise? */ + const float mirroring_min_distance = 15.0; + if (pair_data[0].coc < mirroring_threshold && + (pair_data[1].coc - mirroring_min_distance) > pair_data[0].coc) { + pair_data[1].coc = pair_data[0].coc; + } + else if (pair_data[1].coc < mirroring_threshold && + (pair_data[0].coc - mirroring_min_distance) > pair_data[1].coc) { + pair_data[0].coc = pair_data[1].coc; + } +#endif + + for (int i = 0; i < 2; i++) { + float sample_weight = dof_sample_weight(pair_data[i].coc); + float layer_weight = dof_layer_weight(pair_data[i].coc, is_foreground); + float inter_weight = dof_intersection_weight( + pair_data[i].coc, pair_data[i].dist, intersection_multiplier); + float weight = inter_weight * layer_weight * sample_weight; + + /** + * If a CoC is larger than bordering radius we accumulate it to the general accumulator. + * If not, we accumulate to the ring bucket. This is to have more consistent sample occlusion. + **/ + float accum_weight = dof_gather_accum_weight(pair_data[i].coc, bordering_radius, first_ring); + dof_gather_accumulate_sample(pair_data[i], weight * accum_weight, accum_data); + dof_gather_accumulate_sample(pair_data[i], weight * (1.0 - accum_weight), ring_data); + + accum_data.layer_opacity += layer_weight; + + if (is_foreground) { + ring_data.transparency += 1.0 - inter_weight * layer_weight; + } + else { + float coc = is_foreground ? -pair_data[i].coc : pair_data[i].coc; + ring_data.transparency += saturate(coc - bordering_radius); + } + } +} + +void dof_gather_accumulate_sample_ring(DofGatherData ring_data, + int sample_count, + bool first_ring, + const bool do_fast_gather, + /* accum_data occludes the ring_data if true. */ + const bool reversed_occlusion, + inout DofGatherData accum_data) +{ + if (do_fast_gather) { + /* Do nothing as ring_data contains nothing. All samples are already in + * accum_data. */ + return; + } + + if (first_ring) { + /* Layer opacity is directly accumulated into accum_data data. */ + accum_data.color = ring_data.color; + accum_data.coc = ring_data.coc; + accum_data.coc_sqr = ring_data.coc_sqr; + accum_data.weight = ring_data.weight; + + accum_data.transparency = ring_data.transparency / float(sample_count); + return; + } + + if (ring_data.weight == 0.0) { + return; + } + + float ring_avg_coc = ring_data.coc / ring_data.weight; + float accum_avg_coc = accum_data.coc / accum_data.weight; + + /* Smooth test to set opacity to see if the ring average coc occludes the + * accumulation. Test is reversed to be multiplied against opacity. */ + float ring_occlu = saturate(accum_avg_coc - ring_avg_coc); + /* The bias here is arbitrary. Seems to avoid weird looking foreground in most + * cases. We might need to make it a parameter or find a relative bias. */ + float accum_occlu = saturate((ring_avg_coc - accum_avg_coc) * 0.1 - 1.0); + + if (is_resolve) { + ring_occlu = accum_occlu = 0.0; + } + + if (no_gather_occlusion) { + ring_occlu = 0.0; + accum_occlu = 0.0; + } + + /* (Slide 40) */ + float ring_opacity = saturate(1.0 - ring_data.transparency / float(sample_count)); + float accum_opacity = 1.0 - accum_data.transparency; + + if (reversed_occlusion) { + /* Accum_data occludes the ring. */ + float alpha = (accum_data.weight == 0.0) ? 0.0 : accum_opacity * accum_occlu; + float one_minus_alpha = 1.0 - alpha; + + accum_data.color += ring_data.color * one_minus_alpha; + accum_data.coc += ring_data.coc * one_minus_alpha; + accum_data.coc_sqr += ring_data.coc_sqr * one_minus_alpha; + accum_data.weight += ring_data.weight * one_minus_alpha; + + accum_data.transparency *= 1.0 - ring_opacity; + } + else { + /* Ring occludes the accum_data (Same as reference). */ + float alpha = (accum_data.weight == 0.0) ? 1.0 : (ring_opacity * ring_occlu); + float one_minus_alpha = 1.0 - alpha; + + accum_data.color = accum_data.color * one_minus_alpha + ring_data.color; + accum_data.coc = accum_data.coc * one_minus_alpha + ring_data.coc; + accum_data.coc_sqr = accum_data.coc_sqr * one_minus_alpha + ring_data.coc_sqr; + accum_data.weight = accum_data.weight * one_minus_alpha + ring_data.weight; + } +} + +/* FIXME(fclem) Seems to be wrong since it needs ringcount+1 as input for + * slightfocus gather. */ +/* This should be replaced by web_sample_count_get() but doing so is breaking other things. */ +int dof_gather_total_sample_count(const int ring_count, const int ring_density) +{ + return (ring_count * ring_count - ring_count) * ring_density + 1; +} + +void dof_gather_accumulate_center_sample(DofGatherData center_data, + float bordering_radius, + int i_radius, + const bool do_fast_gather, + const bool is_foreground, + const bool is_resolve, + inout DofGatherData accum_data) +{ + float layer_weight = dof_layer_weight(center_data.coc, is_foreground); + float sample_weight = dof_sample_weight(center_data.coc); + float weight = layer_weight * sample_weight; + float accum_weight = dof_gather_accum_weight(center_data.coc, bordering_radius, false); + + if (do_fast_gather) { + /* Hope for the compiler to optimize the above. */ + layer_weight = 1.0; + sample_weight = 1.0; + accum_weight = 1.0; + weight = 1.0; + } + + center_data.transparency = 1.0 - weight; + + dof_gather_accumulate_sample(center_data, weight * accum_weight, accum_data); + + if (!do_fast_gather) { + if (is_resolve) { + /* NOTE(fclem): Hack to smooth transition to full in-focus opacity. */ + int total_sample_count = dof_gather_total_sample_count(i_radius + 1, + DOF_SLIGHT_FOCUS_DENSITY); + float fac = saturate(1.0 - abs(center_data.coc) / float(dof_layer_threshold)); + accum_data.layer_opacity += float(total_sample_count) * fac * fac; + } + accum_data.layer_opacity += layer_weight; + + /* Logic of dof_gather_accumulate_sample(). */ + weight *= (1.0 - accum_weight); + center_data.coc_sqr = center_data.coc * (center_data.coc * weight); + center_data.color *= weight; + center_data.coc *= weight; + center_data.weight = weight; + + if (is_foreground && !is_resolve) { + /* Reduce issue with closer foreground over distant foreground. */ + float ring_area = sqr(bordering_radius); + dof_gather_ammend_weight(center_data, ring_area); + } + + /* Accumulate center as its own ring. */ + dof_gather_accumulate_sample_ring( + center_data, 1, false, do_fast_gather, is_foreground, accum_data); + } +} + +int dof_gather_total_sample_count_with_density_change(const int ring_count, + const int ring_density, + int density_change) +{ + int sample_count_per_density_change = dof_gather_total_sample_count(ring_count, ring_density) - + dof_gather_total_sample_count( + ring_count - gather_density_change_ring, ring_density); + + return dof_gather_total_sample_count(ring_count, ring_density) + + sample_count_per_density_change * density_change; +} + +void dof_gather_accumulate_resolve(int total_sample_count, + DofGatherData accum_data, + out vec4 out_col, + out float out_weight, + out vec2 out_occlusion) +{ + float weight_inv = safe_rcp(accum_data.weight); + out_col = accum_data.color * weight_inv; + out_occlusion = vec2(abs(accum_data.coc), accum_data.coc_sqr) * weight_inv; + + if (is_foreground) { + out_weight = 1.0 - accum_data.transparency; + } + else if (accum_data.weight > 0.0) { + out_weight = accum_data.layer_opacity / float(total_sample_count); + } + else { + out_weight = 0.0; + } + /* Gathering may not accumulate to 1.0 alpha because of float precision. */ + if (out_weight > 0.99) { + out_weight = 1.0; + } + else if (out_weight < 0.01) { + out_weight = 0.0; + } + /* Same thing for alpha channel. */ + if (out_col.a > 0.993) { + out_col.a = 1.0; + } + else if (out_col.a < 0.003) { + out_col.a = 0.0; + } +} + +float dof_load_gather_coc(sampler2D gather_input_coc_tx, vec2 uv, float lod) +{ + float coc = textureLod(gather_input_coc_tx, uv, lod).r; + /* We gather at halfres. CoC must be divided by 2 to be compared against radii. */ + return coc * 0.5; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Common Gather accumulator. + * \{ */ + +/* Radii needs to be halfres CoC sizes. */ +bool dof_do_density_change(float base_radius, float min_intersectable_radius) +{ + /* Reduce artifact for very large blur. */ + min_intersectable_radius *= 0.1; + + bool need_new_density = (base_radius * unit_ring_radius > min_intersectable_radius); + bool larger_than_min_density = (base_radius * radius_downscale_factor > + float(gather_ring_count)); + + return need_new_density && larger_than_min_density; +} + +void dof_gather_init(float base_radius, + vec2 noise, + out vec2 center_co, + out float lod, + out float intersection_multiplier) +{ + /* Jitter center half a ring to reduce undersampling. */ + vec2 jitter_ofs = 0.499 * sample_disk(noise); + if (DOF_BOKEH_TEXTURE) { + jitter_ofs *= dof_buf.bokeh_anisotropic_scale; + } + vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5; + center_co = frag_coord + jitter_ofs * base_radius * unit_sample_radius; + + /* TODO(fclem) Seems like the default lod selection is too big. Bias to avoid blocky moving out + * of focus shapes. */ + const float lod_bias = -2.0; + lod = max(floor(log2(base_radius * unit_sample_radius) + 0.5) + lod_bias, 0.0); + + if (no_gather_mipmaps) { + lod = 0.0; + } + /* (Slide 64). */ + intersection_multiplier = pow(0.5, lod); +} + +void dof_gather_accumulator(sampler2D color_tx, + sampler2D color_bilinear_tx, + sampler2D coc_tx, + sampler2D bkh_lut_tx, /* Renamed because of ugly macro. */ + float base_radius, + float min_intersectable_radius, + const bool do_fast_gather, + const bool do_density_change, + out vec4 out_color, + out float out_weight, + out vec2 out_occlusion) +{ + vec2 frag_coord = vec2(gl_GlobalInvocationID.xy); + vec2 noise_offset = sampling_rng_2D_get(SAMPLING_LENS_U); + vec2 noise = no_gather_random ? vec2(0.0, 0.0) : + vec2(interlieved_gradient_noise(frag_coord, 0, noise_offset.x), + interlieved_gradient_noise(frag_coord, 1, noise_offset.y)); + + if (!do_fast_gather) { + /* Jitter the radius to reduce noticeable density changes. */ + base_radius += noise.x * unit_ring_radius * base_radius; + } + else { + /* Jittering the radius more than we need means we are going to feather the bokeh shape half a + * ring. So we need to compensate for fast gather that does not check CoC intersection. */ + base_radius += (0.5 - noise.x) * 1.5 * unit_ring_radius * base_radius; + } + /* TODO(fclem) another seed? For now Cranly-Partterson rotation with golden ratio. */ + noise.x = fract(noise.x * 6.1803398875); + + float lod, isect_mul; + vec2 center_co; + dof_gather_init(base_radius, noise, center_co, lod, isect_mul); + + bool first_ring = true; + + DofGatherData accum_data = GATHER_DATA_INIT; + + int density_change = 0; + for (int ring = gather_ring_count; ring > 0; ring--) { + int sample_pair_count = gather_ring_density * ring; + + float step_rot = M_PI / float(sample_pair_count); + mat2 step_rot_mat = rot2_from_angle(step_rot); + + float angle_offset = noise.y * step_rot; + vec2 offset = vec2(cos(angle_offset), sin(angle_offset)); + + float ring_radius = float(ring) * unit_sample_radius * base_radius; + + /* Slide 38. */ + float bordering_radius = ring_radius + + (0.5 + coc_radius_error) * base_radius * unit_sample_radius; + DofGatherData ring_data = GATHER_DATA_INIT; + for (int sample_pair = 0; sample_pair < sample_pair_count; sample_pair++) { + offset = step_rot_mat * offset; + + DofGatherData pair_data[2]; + for (int i = 0; i < 2; i++) { + vec2 offset_co = ((i == 0) ? offset : -offset); + if (DOF_BOKEH_TEXTURE) { + /* Scaling to 0.25 for speed. Improves texture cache hit. */ + offset_co = texture(bkh_lut_tx, offset_co * 0.25 + 0.5).rg; + offset_co *= (is_foreground) ? -dof_buf.bokeh_anisotropic_scale : + dof_buf.bokeh_anisotropic_scale; + } + vec2 sample_co = center_co + offset_co * ring_radius; + vec2 sample_uv = sample_co * dof_buf.gather_uv_fac; + if (do_fast_gather) { + pair_data[i].color = textureLod(color_bilinear_tx, sample_uv, lod); + } + else { + pair_data[i].color = textureLod(color_tx, sample_uv, lod); + } + pair_data[i].coc = dof_load_gather_coc(coc_tx, sample_uv, lod); + pair_data[i].dist = ring_radius; + } + + dof_gather_accumulate_sample_pair(pair_data, + bordering_radius, + isect_mul, + first_ring, + do_fast_gather, + is_foreground, + ring_data, + accum_data); + } + + if (is_foreground) { + /* Reduce issue with closer foreground over distant foreground. */ + /* TODO(fclem) this seems to not be completely correct as the issue remains. */ + float ring_area = (sqr(float(ring) + 0.5 + coc_radius_error) - + sqr(float(ring) - 0.5 + coc_radius_error)) * + sqr(base_radius * unit_sample_radius); + dof_gather_ammend_weight(ring_data, ring_area); + } + + dof_gather_accumulate_sample_ring( + ring_data, sample_pair_count * 2, first_ring, do_fast_gather, is_foreground, accum_data); + + first_ring = false; + + if (do_density_change && (ring == change_density_at_ring) && + (density_change < gather_max_density_change)) { + if (dof_do_density_change(base_radius, min_intersectable_radius)) { + base_radius *= radius_downscale_factor; + ring += gather_density_change_ring; + /* We need to account for the density change in the weights (slide 62). + * For that multiply old kernel data by its area divided by the new kernel area. */ + const float outer_rings_weight = 1.0 / (radius_downscale_factor * radius_downscale_factor); + /* Samples are already weighted per ring in foreground pass. */ + if (!is_foreground) { + dof_gather_ammend_weight(accum_data, outer_rings_weight); + } + /* Re-init kernel position & sampling parameters. */ + dof_gather_init(base_radius, noise, center_co, lod, isect_mul); + density_change++; + } + } + } + + { + /* Center sample. */ + vec2 sample_uv = center_co * dof_buf.gather_uv_fac; + DofGatherData center_data; + if (do_fast_gather) { + center_data.color = textureLod(color_bilinear_tx, sample_uv, lod); + } + else { + center_data.color = textureLod(color_tx, sample_uv, lod); + } + center_data.coc = dof_load_gather_coc(coc_tx, sample_uv, lod); + center_data.dist = 0.0; + + /* Slide 38. */ + float bordering_radius = (0.5 + coc_radius_error) * base_radius * unit_sample_radius; + + dof_gather_accumulate_center_sample( + center_data, bordering_radius, 0, do_fast_gather, is_foreground, false, accum_data); + } + + int total_sample_count = dof_gather_total_sample_count_with_density_change( + gather_ring_count, gather_ring_density, density_change); + dof_gather_accumulate_resolve( + total_sample_count, accum_data, out_color, out_weight, out_occlusion); + + if (debug_gather_perf && density_change > 0) { + float fac = saturate(float(density_change) / float(10.0)); + out_color.rgb = avg(out_color.rgb) * neon_gradient(fac); + } + if (debug_gather_perf && do_fast_gather) { + out_color.rgb = avg(out_color.rgb) * vec3(0.0, 1.0, 0.0); + } + if (debug_scatter_perf) { + out_color.rgb = avg(out_color.rgb) * vec3(0.0, 1.0, 0.0); + } + + /* Output premultiplied color so we can use bilinear sampler in resolve pass. */ + out_color *= out_weight; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Slight focus accumulator. + * + * The full pixel neighborhood is gathered. + * \{ */ + +void dof_slight_focus_gather(sampler2D depth_tx, + sampler2D color_tx, + sampler2D bkh_lut_tx, /* Renamed because of ugly macro job. */ + float radius, + out vec4 out_color, + out float out_weight, + out float out_center_coc) +{ + vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5; + vec2 noise_offset = sampling_rng_2D_get(SAMPLING_LENS_U); + vec2 noise = no_gather_random ? vec2(0.0) : + vec2(interlieved_gradient_noise(frag_coord, 3, noise_offset.x), + interlieved_gradient_noise(frag_coord, 5, noise_offset.y)); + + DofGatherData fg_accum = GATHER_DATA_INIT; + DofGatherData bg_accum = GATHER_DATA_INIT; + + int i_radius = clamp(int(radius), 0, int(dof_layer_threshold)); + + const float sample_count_max = float(DOF_SLIGHT_FOCUS_SAMPLE_MAX); + /* Scale by search area. */ + float sample_count = sample_count_max * saturate(sqr(radius) / sqr(dof_layer_threshold)); + + bool first_ring = true; + + for (float s = 0.0; s < sample_count; s++) { + vec2 rand2 = fract(hammersley_2d(s, sample_count) + noise); + vec2 offset = sample_disk(rand2); + float ring_dist = sqrt(rand2.y); + + DofGatherData pair_data[2]; + for (int i = 0; i < 2; i++) { + vec2 sample_offset = ((i == 0) ? offset : -offset); + /* OPTI: could precompute the factor. */ + vec2 sample_uv = (frag_coord + sample_offset) / vec2(textureSize(depth_tx, 0)); + float depth = textureLod(depth_tx, sample_uv, 0.0).r; + pair_data[i].coc = dof_coc_from_depth(dof_buf, sample_uv, depth); + pair_data[i].color = safe_color(textureLod(color_tx, sample_uv, 0.0)); + pair_data[i].dist = ring_dist; + if (DOF_BOKEH_TEXTURE) { + /* Contains subpixel distance to bokeh shape. */ + ivec2 lut_texel = ivec2(round(sample_offset)) + dof_max_slight_focus_radius; + pair_data[i].dist = texelFetch(bkh_lut_tx, lut_texel, 0).r; + } + pair_data[i].coc = clamp(pair_data[i].coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); + } + + float bordering_radius = ring_dist + 0.5; + const float isect_mul = 1.0; + DofGatherData bg_ring = GATHER_DATA_INIT; + dof_gather_accumulate_sample_pair( + pair_data, bordering_radius, isect_mul, first_ring, false, false, bg_ring, bg_accum); + /* Treat each sample as a ring. */ + dof_gather_accumulate_sample_ring(bg_ring, 2, first_ring, false, false, bg_accum); + + if (DOF_BOKEH_TEXTURE) { + /* Swap distances in order to flip bokeh shape for foreground. */ + float tmp = pair_data[0].dist; + pair_data[0].dist = pair_data[1].dist; + pair_data[1].dist = tmp; + } + DofGatherData fg_ring = GATHER_DATA_INIT; + dof_gather_accumulate_sample_pair( + pair_data, bordering_radius, isect_mul, first_ring, false, true, fg_ring, fg_accum); + /* Treat each sample as a ring. */ + dof_gather_accumulate_sample_ring(fg_ring, 2, first_ring, false, true, fg_accum); + + first_ring = false; + } + + /* Center sample. */ + vec2 sample_uv = frag_coord / vec2(textureSize(depth_tx, 0)); + DofGatherData center_data; + center_data.color = safe_color(textureLod(color_tx, sample_uv, 0.0)); + center_data.coc = dof_coc_from_depth(dof_buf, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r); + center_data.coc = clamp(center_data.coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); + center_data.dist = 0.0; + + out_center_coc = center_data.coc; + + /* Slide 38. */ + float bordering_radius = 0.5; + + dof_gather_accumulate_center_sample( + center_data, bordering_radius, i_radius, false, true, true, fg_accum); + dof_gather_accumulate_center_sample( + center_data, bordering_radius, i_radius, false, false, true, bg_accum); + + vec4 bg_col, fg_col; + float bg_weight, fg_weight; + vec2 unused_occlusion; + + int total_sample_count = int(sample_count) * 2 + 1; + dof_gather_accumulate_resolve(total_sample_count, bg_accum, bg_col, bg_weight, unused_occlusion); + dof_gather_accumulate_resolve(total_sample_count, fg_accum, fg_col, fg_weight, unused_occlusion); + + /* Fix weighting issues on perfectly focus to slight focus transitioning areas. */ + if (abs(center_data.coc) < 0.5) { + bg_col = center_data.color; + bg_weight = 1.0; + } + + /* Alpha Over */ + float alpha = 1.0 - fg_weight; + out_weight = bg_weight * alpha + fg_weight; + out_color = bg_col * bg_weight * alpha + fg_col * fg_weight; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl new file mode 100644 index 00000000000..26a597b04e8 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_bokeh_lut_comp.glsl @@ -0,0 +1,55 @@ + +/** + * Bokeh Look Up Table: This outputs a radius multiplier to shape the sampling in gather pass or + * the scatter sprite appearance. This is only used if bokeh shape is either anamorphic or is not + * a perfect circle. + * We correct samples spacing for polygonal bokeh shapes. However, we do not for anamorphic bokeh + * as it is way more complex and expensive to do. + */ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +void main() +{ + vec2 gather_uv = ((vec2(gl_GlobalInvocationID.xy) + 0.5) / float(DOF_BOKEH_LUT_SIZE)); + /* Center uv in range [-1..1]. */ + gather_uv = gather_uv * 2.0 - 1.0; + + vec2 slight_focus_texel = vec2(gl_GlobalInvocationID.xy) - float(dof_max_slight_focus_radius); + + float radius = length(gather_uv); + + if (dof_buf.bokeh_blades > 0.0) { + /* NOTE: atan(y,x) has output range [-M_PI..M_PI], so add 2pi to avoid negative angles. */ + float theta = atan(gather_uv.y, gather_uv.x) + M_2PI; + float r = length(gather_uv); + + radius /= circle_to_polygon_radius(dof_buf.bokeh_blades, theta - dof_buf.bokeh_rotation); + + float theta_new = circle_to_polygon_angle(dof_buf.bokeh_blades, theta); + float r_new = circle_to_polygon_radius(dof_buf.bokeh_blades, theta_new); + + theta_new -= dof_buf.bokeh_rotation; + + gather_uv = r_new * vec2(-cos(theta_new), sin(theta_new)); + + { + /* Slight focus distance */ + slight_focus_texel *= dof_buf.bokeh_anisotropic_scale_inv; + float theta = atan(slight_focus_texel.y, -slight_focus_texel.x) + M_2PI; + slight_focus_texel /= circle_to_polygon_radius(dof_buf.bokeh_blades, + theta + dof_buf.bokeh_rotation); + } + } + else { + gather_uv *= safe_rcp(length(gather_uv)); + } + + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + /* For gather store the normalized UV. */ + imageStore(out_gather_lut_img, texel, gather_uv.xyxy); + /* For scatter store distance. LUT will be scaled by COC. */ + imageStore(out_scatter_lut_img, texel, vec4(radius)); + /* For slight focus gather store pixel perfect distance. */ + imageStore(out_resolve_lut_img, texel, vec4(length(slight_focus_texel))); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl new file mode 100644 index 00000000000..3d45f285da9 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_downsample_comp.glsl @@ -0,0 +1,32 @@ + +/** + * Downsample pass: CoC aware downsample to quarter resolution. + * + * Pretty much identical to the setup pass but get CoC from buffer. + * Also does not weight luma for the bilateral weights. + */ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +void main() +{ + vec2 halfres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy); + /* Center uv around the 4 halfres pixels. */ + vec2 quad_center = vec2(gl_GlobalInvocationID * 2 + 1) * halfres_texel_size; + + vec4 colors[4]; + vec4 cocs; + for (int i = 0; i < 4; i++) { + vec2 sample_uv = quad_center + quad_offsets[i] * halfres_texel_size; + colors[i] = textureLod(color_tx, sample_uv, 0.0); + cocs[i] = textureLod(coc_tx, sample_uv, 0.0).r; + } + + vec4 weights = dof_bilateral_coc_weights(cocs); + /* Normalize so that the sum is 1. */ + weights *= safe_rcp(sum(weights)); + + vec4 out_color = weighted_sum_array(colors, weights); + + imageStore(out_color_img, ivec2(gl_GlobalInvocationID.xy), out_color); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl new file mode 100644 index 00000000000..67e6c8ec7d2 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_filter_comp.glsl @@ -0,0 +1,163 @@ + +/** + * Gather Filter pass: Filter the gather pass result to reduce noise. + * + * This is a simple 3x3 median filter to avoid dilating highlights with a 3x3 max filter even if + * cheaper. + */ + +struct FilterSample { + vec4 color; + float weight; +}; + +/* -------------------------------------------------------------------- */ +/** \name Pixel cache. + * \{ */ + +const uint cache_size = gl_WorkGroupSize.x + 2; +shared vec4 color_cache[cache_size][cache_size]; +shared float weight_cache[cache_size][cache_size]; + +void cache_init() +{ + /** + * Load enough values into LDS to perform the filter. + * + * ┌──────────────────────────────┐ + * │ │ < Border texels that needs to be loaded. + * │ x x x x x x x x │ ─┐ + * │ x x x x x x x x │ │ + * │ x x x x x x x x │ │ + * │ x x x x x x x x │ │ Thread Group Size 8x8. + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ ─┘ + * │ L L L L L │ < Border texels that needs to be loaded. + * └──────────────────────────────┘ + * └───────────┘ + * Load using 5x5 threads. + */ + + ivec2 texel = ivec2(gl_GlobalInvocationID.xy) - 1; + if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_size / 2u)))) { + for (int y = 0; y < 2; y++) { + for (int x = 0; x < 2; x++) { + ivec2 offset = ivec2(x, y) * ivec2(cache_size / 2u); + ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset; + ivec2 load_texel = clamp(texel + offset, ivec2(0), textureSize(color_tx, 0) - 1); + + color_cache[cache_texel.y][cache_texel.x] = texelFetch(color_tx, load_texel, 0); + weight_cache[cache_texel.y][cache_texel.x] = texelFetch(weight_tx, load_texel, 0).r; + } + } + } + barrier(); +} + +FilterSample cache_sample(int x, int y) +{ + return FilterSample(color_cache[y][x], weight_cache[y][x]); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Median filter + * From: + * Implementing Median Filters in XC4000E FPGAs + * JOHN L. SMITH, Univision Technologies Inc., Billerica, MA + * http://users.utcluj.ro/~baruch/resources/Image/xl23_16.pdf + * Figure 1 + * \{ */ + +FilterSample filter_min(FilterSample a, FilterSample b) +{ + return FilterSample(min(a.color, b.color), min(a.weight, b.weight)); +} + +FilterSample filter_max(FilterSample a, FilterSample b) +{ + return FilterSample(max(a.color, b.color), max(a.weight, b.weight)); +} + +FilterSample filter_min(FilterSample a, FilterSample b, FilterSample c) +{ + return FilterSample(min(a.color, min(c.color, b.color)), min(a.weight, min(c.weight, b.weight))); +} + +FilterSample filter_max(FilterSample a, FilterSample b, FilterSample c) +{ + return FilterSample(max(a.color, max(c.color, b.color)), max(a.weight, max(c.weight, b.weight))); +} + +FilterSample filter_median(FilterSample s1, FilterSample s2, FilterSample s3) +{ + /* From diagram, with nodes numbered from top to bottom. */ + FilterSample l1 = filter_min(s2, s3); + FilterSample h1 = filter_max(s2, s3); + FilterSample h2 = filter_max(s1, l1); + FilterSample l3 = filter_min(h2, h1); + return l3; +} + +struct FilterLmhResult { + FilterSample low; + FilterSample median; + FilterSample high; +}; + +FilterLmhResult filter_lmh(FilterSample s1, FilterSample s2, FilterSample s3) +{ + /* From diagram, with nodes numbered from top to bottom. */ + FilterSample h1 = filter_max(s2, s3); + FilterSample l1 = filter_min(s2, s3); + + FilterSample h2 = filter_max(s1, l1); + FilterSample l2 = filter_min(s1, l1); + + FilterSample h3 = filter_max(h2, h1); + FilterSample l3 = filter_min(h2, h1); + + FilterLmhResult result; + result.low = l2; + result.median = l3; + result.high = h3; + + return result; +} + +/** \} */ + +void main() +{ + /** + * NOTE: We can **NOT** optimize by discarding some tiles as the result is sampled using bilinear + * filtering in the resolve pass. Not outputting to a tile means that border texels have undefined + * value and tile border will be noticeable in the final image. + */ + + cache_init(); + + ivec2 texel = ivec2(gl_LocalInvocationID.xy); + + FilterLmhResult rows[3]; + for (int y = 0; y < 3; y++) { + rows[y] = filter_lmh(cache_sample(texel.x + 0, texel.y + y), + cache_sample(texel.x + 1, texel.y + y), + cache_sample(texel.x + 2, texel.y + y)); + } + /* Left nodes. */ + FilterSample high = filter_max(rows[0].low, rows[1].low, rows[2].low); + /* Right nodes. */ + FilterSample low = filter_min(rows[0].high, rows[1].high, rows[2].high); + /* Center nodes. */ + FilterSample median = filter_median(rows[0].median, rows[1].median, rows[2].median); + /* Last bottom nodes. */ + median = filter_median(low, median, high); + + ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy); + imageStore(out_color_img, out_texel, median.color); + imageStore(out_weight_img, out_texel, vec4(median.weight)); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl new file mode 100644 index 00000000000..201e8ac9ba1 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_gather_comp.glsl @@ -0,0 +1,99 @@ + +/** + * Gather pass: Convolve foreground and background parts in separate passes. + * + * Using the min&max CoC tile buffer, we select the best appropriate method to blur the scene color. + * A fast gather path is taken if there is not many CoC variation inside the tile. + * + * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring + * rotation to ensure maximum coverage. + * + * Outputs: + * - Color * Weight, Weight, Occlusion 'CoC' Depth (mean and variance) + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl) + +void main() +{ + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy / DOF_TILES_SIZE); + CocTile coc_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, tile_co); + CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); + + float base_radius, min_radius, min_intersectable_radius; + bool can_early_out; + if (is_foreground) { + base_radius = -coc_tile.fg_min_coc; + min_radius = -coc_tile.fg_max_coc; + min_intersectable_radius = -coc_tile.fg_max_intersectable_coc; + can_early_out = !prediction.do_foreground; + } + else { + base_radius = coc_tile.bg_max_coc; + min_radius = coc_tile.bg_min_coc; + min_intersectable_radius = coc_tile.bg_min_intersectable_coc; + can_early_out = !prediction.do_background; + } + + bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground); + + /* Gather at half resolution. Divide CoC by 2. */ + base_radius *= 0.5; + min_intersectable_radius *= 0.5; + + bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius); + + vec4 out_color; + float out_weight; + vec2 out_occlusion; + + if (can_early_out) { + out_color = vec4(0.0); + out_weight = 0.0; + out_occlusion = vec2(0.0, 0.0); + } + else if (do_fast_gather) { + dof_gather_accumulator(color_tx, + color_bilinear_tx, + coc_tx, + bokeh_lut_tx, + base_radius, + min_intersectable_radius, + true, + false, + out_color, + out_weight, + out_occlusion); + } + else if (do_density_change) { + dof_gather_accumulator(color_tx, + color_bilinear_tx, + coc_tx, + bokeh_lut_tx, + base_radius, + min_intersectable_radius, + false, + true, + out_color, + out_weight, + out_occlusion); + } + else { + dof_gather_accumulator(color_tx, + color_bilinear_tx, + coc_tx, + bokeh_lut_tx, + base_radius, + min_intersectable_radius, + false, + false, + out_color, + out_weight, + out_occlusion); + } + + ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy); + imageStore(out_color_img, out_texel, out_color); + imageStore(out_weight_img, out_texel, vec4(out_weight)); + imageStore(out_occlusion_img, out_texel, out_occlusion.xyxy); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl new file mode 100644 index 00000000000..1b42d21ed65 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_hole_fill_comp.glsl @@ -0,0 +1,70 @@ + +/** + * Holefill pass: Gather background parts where foreground is present. + * + * Using the min&max CoC tile buffer, we select the best appropriate method to blur the scene color. + * A fast gather path is taken if there is not many CoC variation inside the tile. + * + * We sample using an octaweb sampling pattern. We randomize the kernel center and each ring + * rotation to ensure maximum coverage. + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl) + +void main() +{ + ivec2 tile_co = ivec2(gl_GlobalInvocationID.xy / DOF_TILES_SIZE); + CocTile coc_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, tile_co); + CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); + + float base_radius = -coc_tile.fg_min_coc; + float min_radius = -coc_tile.fg_max_coc; + float min_intersectable_radius = dof_tile_large_coc; + bool can_early_out = !prediction.do_hole_fill; + + bool do_fast_gather = dof_do_fast_gather(base_radius, min_radius, is_foreground); + + /* Gather at half resolution. Divide CoC by 2. */ + base_radius *= 0.5; + min_intersectable_radius *= 0.5; + + bool do_density_change = dof_do_density_change(base_radius, min_intersectable_radius); + + vec4 out_color = vec4(0.0); + float out_weight = 0.0; + vec2 unused_occlusion = vec2(0.0, 0.0); + + if (can_early_out) { + /* Early out. */ + } + else if (do_fast_gather) { + dof_gather_accumulator(color_tx, + color_bilinear_tx, + coc_tx, + coc_tx, + base_radius, + min_intersectable_radius, + true, + false, + out_color, + out_weight, + unused_occlusion); + } + else { + dof_gather_accumulator(color_tx, + color_bilinear_tx, + coc_tx, + coc_tx, + base_radius, + min_intersectable_radius, + false, + false, + out_color, + out_weight, + unused_occlusion); + } + + ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy); + imageStore(out_color_img, out_texel, out_color); + imageStore(out_weight_img, out_texel, vec4(out_weight)); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl new file mode 100644 index 00000000000..f89da641446 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_lib.glsl @@ -0,0 +1,327 @@ + +/** + * Depth of Field utils. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Constants. + * \{ */ + +#ifndef DOF_SLIGHT_FOCUS_DENSITY +# define DOF_SLIGHT_FOCUS_DENSITY 2 +#endif + +#ifdef DOF_RESOLVE_PASS +const bool is_resolve = true; +#else +const bool is_resolve = false; +#endif +#ifdef DOF_FOREGROUND_PASS +const bool is_foreground = DOF_FOREGROUND_PASS; +#else +const bool is_foreground = false; +#endif +/* Debug options */ +const bool debug_gather_perf = false; +const bool debug_scatter_perf = false; +const bool debug_resolve_perf = false; + +const bool no_smooth_intersection = false; +const bool no_gather_occlusion = false; +const bool no_gather_mipmaps = false; +const bool no_gather_random = false; +const bool no_gather_filtering = false; +const bool no_scatter_occlusion = false; +const bool no_scatter_pass = false; +const bool no_foreground_pass = false; +const bool no_background_pass = false; +const bool no_slight_focus_pass = false; +const bool no_focus_pass = false; +const bool no_hole_fill_pass = false; + +/* Distribute weights between near/slightfocus/far fields (slide 117). */ +const float dof_layer_threshold = 4.0; +/* Make sure it overlaps. */ +const float dof_layer_offset_fg = 0.5 + 1.0; +/* Extra offset for convolution layers to avoid light leaking from background. */ +const float dof_layer_offset = 0.5 + 0.5; + +const int dof_max_slight_focus_radius = DOF_MAX_SLIGHT_FOCUS_RADIUS; + +const vec2 quad_offsets[4] = vec2[4]( + vec2(-0.5, 0.5), vec2(0.5, 0.5), vec2(0.5, -0.5), vec2(-0.5, -0.5)); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Weighting and downsampling utils. + * \{ */ + +float dof_hdr_color_weight(vec4 color) +{ + /* Very fast "luma" weighting. */ + float luma = (color.g * 2.0) + (color.r + color.b); + /* TODO(fclem) Pass correct exposure. */ + const float exposure = 1.0; + return 1.0 / (luma * exposure + 4.0); +} + +float dof_coc_select(vec4 cocs) +{ + /* Select biggest coc. */ + float selected_coc = cocs.x; + if (abs(cocs.y) > abs(selected_coc)) { + selected_coc = cocs.y; + } + if (abs(cocs.z) > abs(selected_coc)) { + selected_coc = cocs.z; + } + if (abs(cocs.w) > abs(selected_coc)) { + selected_coc = cocs.w; + } + return selected_coc; +} + +/* NOTE: Do not forget to normalize weights afterwards. */ +vec4 dof_bilateral_coc_weights(vec4 cocs) +{ + float chosen_coc = dof_coc_select(cocs); + + const float scale = 4.0; /* TODO(fclem) revisit. */ + /* NOTE: The difference between the cocs should be inside a abs() function, + * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). */ + return saturate(1.0 - (chosen_coc - cocs) * scale); +} + +/* NOTE: Do not forget to normalize weights afterwards. */ +vec4 dof_bilateral_color_weights(vec4 colors[4]) +{ + vec4 weights; + for (int i = 0; i < 4; i++) { + weights[i] = dof_hdr_color_weight(colors[i]); + } + return weights; +} + +/* Returns signed Circle of confusion radius (in pixel) based on depth buffer value [0..1]. */ +float dof_coc_from_depth(DepthOfFieldData dof_data, vec2 uv, float depth) +{ + if (is_panoramic(dof_data.camera_type)) { + /* Use radial depth. */ + depth = -length(get_view_space_from_depth(uv, depth)); + } + else { + depth = get_view_z_from_depth(depth); + } + return coc_radius_from_camera_depth(dof_data, depth); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gather & Scatter Weighting + * \{ */ + +float dof_layer_weight(float coc, const bool is_foreground) +{ + /* NOTE: These are fullres pixel CoC value. */ + if (is_resolve) { + return saturate(-abs(coc) + dof_layer_threshold + dof_layer_offset) * + float(is_foreground ? (coc <= 0.5) : (coc > -0.5)); + } + else { + coc *= 2.0; /* Account for half pixel gather. */ + float threshold = dof_layer_threshold - + ((is_foreground) ? dof_layer_offset_fg : dof_layer_offset); + return saturate(((is_foreground) ? -coc : coc) - threshold); + } +} +vec4 dof_layer_weight(vec4 coc) +{ + /* NOTE: Used for scatter pass which already flipped the sign correctly. */ + coc *= 2.0; /* Account for half pixel gather. */ + return saturate(coc - dof_layer_threshold + dof_layer_offset); +} + +/* NOTE: This is halfres CoC radius. */ +float dof_sample_weight(float coc) +{ +#if 1 /* Optimized */ + return min(1.0, 1.0 / sqr(coc)); +#else + /* Full intensity if CoC radius is below the pixel footprint. */ + const float min_coc = 1.0; + coc = max(min_coc, abs(coc)); + return (M_PI * min_coc * min_coc) / (M_PI * coc * coc); +#endif +} +vec4 dof_sample_weight(vec4 coc) +{ +#if 1 /* Optimized */ + return min(vec4(1.0), 1.0 / sqr(coc)); +#else + /* Full intensity if CoC radius is below the pixel footprint. */ + const float min_coc = 1.0; + coc = max(vec4(min_coc), abs(coc)); + return (M_PI * min_coc * min_coc) / (M_PI * coc * coc); +#endif +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Circle of Confusion tiles + * \{ */ + +struct CocTile { + float fg_min_coc; + float fg_max_coc; + float fg_max_intersectable_coc; + float bg_min_coc; + float bg_max_coc; + float bg_min_intersectable_coc; +}; + +/* WATCH: Might have to change depending on the texture format. */ +const float dof_tile_large_coc = 1024.0; + +/* Init a CoC tile for reduction algorithms. */ +CocTile dof_coc_tile_init() +{ + CocTile tile; + tile.fg_min_coc = 0.0; + tile.fg_max_coc = -dof_tile_large_coc; + tile.fg_max_intersectable_coc = dof_tile_large_coc; + tile.bg_min_coc = dof_tile_large_coc; + tile.bg_max_coc = 0.0; + tile.bg_min_intersectable_coc = dof_tile_large_coc; + return tile; +} + +CocTile dof_coc_tile_unpack(vec3 fg, vec3 bg) +{ + CocTile tile; + tile.fg_min_coc = -fg.x; + tile.fg_max_coc = -fg.y; + tile.fg_max_intersectable_coc = -fg.z; + tile.bg_min_coc = bg.x; + tile.bg_max_coc = bg.y; + tile.bg_min_intersectable_coc = bg.z; + return tile; +} + +/* WORKAROUND(fclem): GLSL compilers differs in what qualifiers are requires to pass images as + * parameters. Workaround by using defines. */ +#define dof_coc_tile_load(tiles_fg_img_, tiles_bg_img_, texel_) \ + dof_coc_tile_unpack( \ + imageLoad(tiles_fg_img_, clamp(texel_, ivec2(0), imageSize(tiles_fg_img_) - 1)).xyz, \ + imageLoad(tiles_bg_img_, clamp(texel_, ivec2(0), imageSize(tiles_bg_img_) - 1)).xyz) + +void dof_coc_tile_pack(CocTile tile, out vec3 out_fg, out vec3 out_bg) +{ + out_fg.x = -tile.fg_min_coc; + out_fg.y = -tile.fg_max_coc; + out_fg.z = -tile.fg_max_intersectable_coc; + out_bg.x = tile.bg_min_coc; + out_bg.y = tile.bg_max_coc; + out_bg.z = tile.bg_min_intersectable_coc; +} + +#define dof_coc_tile_store(tiles_fg_img_, tiles_bg_img_, texel_out_, tile_data_) \ + if (true) { \ + vec3 out_fg; \ + vec3 out_bg; \ + dof_coc_tile_pack(tile_data_, out_fg, out_bg); \ + imageStore(tiles_fg_img_, texel_out_, out_fg.xyzz); \ + imageStore(tiles_bg_img_, texel_out_, out_bg.xyzz); \ + } + +bool dof_do_fast_gather(float max_absolute_coc, float min_absolute_coc, const bool is_foreground) +{ + float min_weight = dof_layer_weight((is_foreground) ? -min_absolute_coc : min_absolute_coc, + is_foreground); + if (min_weight < 1.0) { + return false; + } + /* FIXME(fclem): This is a workaround to fast gather triggering too early. Since we use custom + * opacity mask, the opacity is not given to be 100% even for after normal threshold. */ + if (is_foreground && min_absolute_coc < dof_layer_threshold) { + return false; + } + return (max_absolute_coc - min_absolute_coc) < (DOF_FAST_GATHER_COC_ERROR * max_absolute_coc); +} + +struct CocTilePrediction { + bool do_foreground; + bool do_slight_focus; + bool do_focus; + bool do_background; + bool do_hole_fill; +}; + +/** + * Using the tile CoC infos, predict which convolutions are required and the ones that can be + * skipped. + */ +CocTilePrediction dof_coc_tile_prediction_get(CocTile tile) +{ + /* Based on tile value, predict what pass we need to load. */ + CocTilePrediction predict; + + predict.do_foreground = (-tile.fg_min_coc > dof_layer_threshold - dof_layer_offset_fg); + bool fg_fully_opaque = predict.do_foreground && + dof_do_fast_gather(-tile.fg_min_coc, -tile.fg_max_coc, true); + predict.do_background = !fg_fully_opaque && + (tile.bg_max_coc > dof_layer_threshold - dof_layer_offset); + bool bg_fully_opaque = predict.do_background && + dof_do_fast_gather(-tile.bg_max_coc, tile.bg_min_coc, false); + predict.do_hole_fill = !fg_fully_opaque && -tile.fg_min_coc > 0.0; + predict.do_focus = !fg_fully_opaque; + predict.do_slight_focus = !fg_fully_opaque; + +#if 0 /* Debug */ + predict.do_foreground = predict.do_background = predict.do_hole_fill = true; +#endif + return predict; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gathering + * \{ */ + +/** + * Generate samples in a square pattern with the ring radius. X is the center tile. + * + * Dist1 Dist2 + * 6 5 4 3 2 + * 3 2 1 7 1 + * . X 0 . X 0 + * . . . . . + * . . . . . + * + * Samples are expected to be mirrored to complete the pattern. + **/ +ivec2 dof_square_ring_sample_offset(int ring_distance, int sample_id) +{ + ivec2 offset; + if (sample_id < ring_distance) { + offset.x = ring_distance; + offset.y = sample_id; + } + else if (sample_id < ring_distance * 3) { + offset.x = ring_distance - sample_id + ring_distance; + offset.y = ring_distance; + } + else { + offset.x = -ring_distance; + offset.y = ring_distance - sample_id + 3 * ring_distance; + } + return offset; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl new file mode 100644 index 00000000000..80555367478 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_reduce_comp.glsl @@ -0,0 +1,247 @@ + +/** + * Reduce copy pass: filter fireflies and split color between scatter and gather input. + * + * NOTE: The texture can end up being too big because of the mipmap padding. We correct for + * that during the convolution phase. + * + * Inputs: + * - Output of setup pass (halfres) and reduce downsample pass (quarter res). + * Outputs: + * - Halfres padded to avoid mipmap misalignment (so possibly not matching input size). + * - Gather input color (whole mip chain), Scatter rect list, Signed CoC (whole mip chain). + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +/* NOTE: Do not compare alpha as it is not scattered by the scatter pass. */ +float dof_scatter_neighborhood_rejection(vec3 color) +{ + color = min(vec3(dof_buf.scatter_neighbor_max_color), color); + + float validity = 0.0; + + /* Centered in the middle of 4 quarter res texel. */ + vec2 texel_size = 1.0 / vec2(textureSize(downsample_tx, 0).xy); + vec2 uv = ((vec2(gl_GlobalInvocationID.xy) + 0.5) * 0.5) * texel_size; + + vec3 max_diff = vec3(0.0); + for (int i = 0; i < 4; i++) { + vec2 sample_uv = uv + quad_offsets[i] * texel_size; + vec3 ref = textureLod(downsample_tx, sample_uv, 0.0).rgb; + + ref = min(vec3(dof_buf.scatter_neighbor_max_color), ref); + float diff = max_v3(max(vec3(0.0), abs(ref - color))); + + const float rejection_threshold = 0.7; + diff = saturate(diff / rejection_threshold - 1.0); + validity = max(validity, diff); + } + + return validity; +} + +/* This avoids Bokeh sprite popping in and out at the screen border and + * drawing Bokeh sprites larger than the screen. */ +float dof_scatter_screen_border_rejection(float coc, ivec2 texel) +{ + vec2 screen_size = vec2(imageSize(inout_color_lod0_img)); + vec2 uv = (vec2(texel) + 0.5) / screen_size; + vec2 screen_pos = uv * screen_size; + float min_screen_border_distance = min_v2(min(screen_pos, screen_size - screen_pos)); + /* Fullres to halfres CoC. */ + coc *= 0.5; + /* Allow 10px transition. */ + const float rejection_hardeness = 1.0 / 10.0; + return saturate((min_screen_border_distance - abs(coc)) * rejection_hardeness + 1.0); +} + +float dof_scatter_luminosity_rejection(vec3 color) +{ + const float rejection_hardness = 1.0; + return saturate(max_v3(color - dof_buf.scatter_color_threshold) * rejection_hardness); +} + +float dof_scatter_coc_radius_rejection(float coc) +{ + const float rejection_hardness = 0.3; + return saturate((abs(coc) - dof_buf.scatter_coc_threshold) * rejection_hardness); +} + +float fast_luma(vec3 color) +{ + return (2.0 * color.g) + color.r + color.b; +} + +const uint cache_size = gl_WorkGroupSize.x; +shared vec4 color_cache[cache_size][cache_size]; +shared float coc_cache[cache_size][cache_size]; +shared float do_scatter[cache_size][cache_size]; + +void main() +{ + ivec2 texel = min(ivec2(gl_GlobalInvocationID.xy), imageSize(inout_color_lod0_img) - 1); + uvec2 texel_local = gl_LocalInvocationID.xy; + /* Increase readablility. */ +#define LOCAL_INDEX texel_local.y][texel_local.x +#define LOCAL_OFFSET(x_, y_) texel_local.y + (y_)][texel_local.x + (x_) + + /* Load level 0 into cache. */ + color_cache[LOCAL_INDEX] = imageLoad(inout_color_lod0_img, texel); + coc_cache[LOCAL_INDEX] = imageLoad(in_coc_lod0_img, texel).r; + + /* Only scatter if luminous enough. */ + do_scatter[LOCAL_INDEX] = dof_scatter_luminosity_rejection(color_cache[LOCAL_INDEX].rgb); + /* Only scatter if CoC is big enough. */ + do_scatter[LOCAL_INDEX] *= dof_scatter_coc_radius_rejection(coc_cache[LOCAL_INDEX]); + /* Only scatter if CoC is not too big to avoid performance issues. */ + do_scatter[LOCAL_INDEX] *= dof_scatter_screen_border_rejection(coc_cache[LOCAL_INDEX], texel); + /* Only scatter if neighborhood is different enough. */ + do_scatter[LOCAL_INDEX] *= dof_scatter_neighborhood_rejection(color_cache[LOCAL_INDEX].rgb); + /* For debugging. */ + if (no_scatter_pass) { + do_scatter[LOCAL_INDEX] = 0.0; + } + + barrier(); + + /* Add a scatter sprite for each 2x2 pixel neighborhood passing the threshold. */ + if (all(equal(texel_local & 1u, uvec2(0)))) { + vec4 do_scatter4; + /* Follows quad_offsets order. */ + do_scatter4.x = do_scatter[LOCAL_OFFSET(0, 1)]; + do_scatter4.y = do_scatter[LOCAL_OFFSET(1, 1)]; + do_scatter4.z = do_scatter[LOCAL_OFFSET(1, 0)]; + do_scatter4.w = do_scatter[LOCAL_OFFSET(0, 0)]; + if (any(greaterThan(do_scatter4, vec4(0.0)))) { + /* Apply energy conservation to anamorphic scattered bokeh. */ + do_scatter4 *= max_v2(dof_buf.bokeh_anisotropic_scale_inv); + + /* Circle of Confusion. */ + vec4 coc4; + coc4.x = coc_cache[LOCAL_OFFSET(0, 1)]; + coc4.y = coc_cache[LOCAL_OFFSET(1, 1)]; + coc4.z = coc_cache[LOCAL_OFFSET(1, 0)]; + coc4.w = coc_cache[LOCAL_OFFSET(0, 0)]; + /* We are scattering at half resolution, so divide CoC by 2. */ + coc4 *= 0.5; + /* Sprite center position. Center sprite around the 4 texture taps. */ + vec2 offset = vec2(gl_GlobalInvocationID.xy) + 1; + /* Add 2.5 to max_coc because the max_coc may not be centered on the sprite origin + * and because we smooth the bokeh shape a bit in the pixel shader. */ + vec2 half_extent = max_v4(abs(coc4)) * dof_buf.bokeh_anisotropic_scale + 2.5; + /* Issue a sprite for each field if any CoC matches. */ + if (any(lessThan(do_scatter4 * sign(coc4), vec4(0.0)))) { + /* Same value for all threads. Not an issue if we don't sync access to it. */ + scatter_fg_indirect_buf.v_count = 4u; + /* Issue 1 strip instance per sprite. */ + uint rect_id = atomicAdd(scatter_fg_indirect_buf.i_count, 1u); + if (rect_id < dof_buf.scatter_max_rect) { + + vec4 coc4_fg = max(vec4(0.0), -coc4); + vec4 fg_weights = dof_layer_weight(coc4_fg) * dof_sample_weight(coc4_fg) * do_scatter4; + /* Filter NaNs. */ + fg_weights = select(fg_weights, vec4(0.0), equal(coc4_fg, vec4(0.0))); + + ScatterRect rect_fg; + rect_fg.offset = offset; + /* Negate extent to flip the sprite. Mimics optical phenomenon. */ + rect_fg.half_extent = -half_extent; + /* NOTE: Since we fliped the quad along (1,-1) line, we need to also swap the (1,1) and + * (0,0) values so that quad_offsets is in the right order in the vertex shader. */ + + /* Circle of Confusion absolute radius in halfres pixels. */ + rect_fg.color_and_coc[0].a = coc4_fg[0]; + rect_fg.color_and_coc[1].a = coc4_fg[3]; + rect_fg.color_and_coc[2].a = coc4_fg[2]; + rect_fg.color_and_coc[3].a = coc4_fg[1]; + /* Apply weights. */ + rect_fg.color_and_coc[0].rgb = color_cache[LOCAL_OFFSET(0, 1)].rgb * fg_weights[0]; + rect_fg.color_and_coc[1].rgb = color_cache[LOCAL_OFFSET(0, 0)].rgb * fg_weights[3]; + rect_fg.color_and_coc[2].rgb = color_cache[LOCAL_OFFSET(1, 0)].rgb * fg_weights[2]; + rect_fg.color_and_coc[3].rgb = color_cache[LOCAL_OFFSET(1, 1)].rgb * fg_weights[1]; + + scatter_fg_list_buf[rect_id] = rect_fg; + } + } + if (any(greaterThan(do_scatter4 * sign(coc4), vec4(0.0)))) { + /* Same value for all threads. Not an issue if we don't sync access to it. */ + scatter_bg_indirect_buf.v_count = 4u; + /* Issue 1 strip instance per sprite. */ + uint rect_id = atomicAdd(scatter_bg_indirect_buf.i_count, 1u); + if (rect_id < dof_buf.scatter_max_rect) { + vec4 coc4_bg = max(vec4(0.0), coc4); + vec4 bg_weights = dof_layer_weight(coc4_bg) * dof_sample_weight(coc4_bg) * do_scatter4; + /* Filter NaNs. */ + bg_weights = select(bg_weights, vec4(0.0), equal(coc4_bg, vec4(0.0))); + + ScatterRect rect_bg; + rect_bg.offset = offset; + rect_bg.half_extent = half_extent; + + /* Circle of Confusion absolute radius in halfres pixels. */ + rect_bg.color_and_coc[0].a = coc4_bg[0]; + rect_bg.color_and_coc[1].a = coc4_bg[1]; + rect_bg.color_and_coc[2].a = coc4_bg[2]; + rect_bg.color_and_coc[3].a = coc4_bg[3]; + /* Apply weights. */ + rect_bg.color_and_coc[0].rgb = color_cache[LOCAL_OFFSET(0, 1)].rgb * bg_weights[0]; + rect_bg.color_and_coc[1].rgb = color_cache[LOCAL_OFFSET(1, 1)].rgb * bg_weights[1]; + rect_bg.color_and_coc[2].rgb = color_cache[LOCAL_OFFSET(1, 0)].rgb * bg_weights[2]; + rect_bg.color_and_coc[3].rgb = color_cache[LOCAL_OFFSET(0, 0)].rgb * bg_weights[3]; + + scatter_bg_list_buf[rect_id] = rect_bg; + } + } + } + } + + /* Remove scatter color from gather. */ + color_cache[LOCAL_INDEX].rgb *= 1.0 - do_scatter[LOCAL_INDEX]; + imageStore(inout_color_lod0_img, texel, color_cache[LOCAL_INDEX]); + + /* Recursive downsample. */ + for (uint i = 1u; i < DOF_MIP_COUNT; i++) { + barrier(); + uint mask = ~(~0u << i); + if (all(equal(gl_LocalInvocationID.xy & mask, uvec2(0)))) { + uint ofs = 1u << (i - 1u); + + /* TODO(fclem): Could use wave shuffle intrinsics to avoid LDS as suggested by the paper. */ + vec4 coc4; + coc4.x = coc_cache[LOCAL_OFFSET(0, ofs)]; + coc4.y = coc_cache[LOCAL_OFFSET(ofs, ofs)]; + coc4.z = coc_cache[LOCAL_OFFSET(ofs, 0)]; + coc4.w = coc_cache[LOCAL_OFFSET(0, 0)]; + + vec4 colors[4]; + colors[0] = color_cache[LOCAL_OFFSET(0, ofs)]; + colors[1] = color_cache[LOCAL_OFFSET(ofs, ofs)]; + colors[2] = color_cache[LOCAL_OFFSET(ofs, 0)]; + colors[3] = color_cache[LOCAL_OFFSET(0, 0)]; + + vec4 weights = dof_bilateral_coc_weights(coc4); + weights *= dof_bilateral_color_weights(colors); + /* Normalize so that the sum is 1. */ + weights *= safe_rcp(sum(weights)); + + color_cache[LOCAL_INDEX] = weighted_sum_array(colors, weights); + coc_cache[LOCAL_INDEX] = dot(coc4, weights); + + ivec2 texel = ivec2(gl_GlobalInvocationID.xy >> i); + + if (i == 1) { + imageStore(out_color_lod1_img, texel, color_cache[LOCAL_INDEX]); + imageStore(out_coc_lod1_img, texel, vec4(coc_cache[LOCAL_INDEX])); + } + else if (i == 2) { + imageStore(out_color_lod2_img, texel, color_cache[LOCAL_INDEX]); + imageStore(out_coc_lod2_img, texel, vec4(coc_cache[LOCAL_INDEX])); + } + else /* if (i == 3) */ { + imageStore(out_color_lod3_img, texel, color_cache[LOCAL_INDEX]); + imageStore(out_coc_lod3_img, texel, vec4(coc_cache[LOCAL_INDEX])); + } + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl new file mode 100644 index 00000000000..8873a9da235 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_resolve_comp.glsl @@ -0,0 +1,178 @@ + +/** + * Recombine Pass: Load separate convolution layer and composite with self + * slight defocus convolution and in-focus fields. + * + * The halfres gather methods are fast but lack precision for small CoC areas. + * To fix this we do a bruteforce gather to have a smooth transition between + * in-focus and defocus regions. + */ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_accumulator_lib.glsl) + +shared uint shared_max_slight_focus_abs_coc; + +/** + * Returns The max CoC in the Slight Focus range inside this compute tile. + */ +float dof_slight_focus_coc_tile_get(vec2 frag_coord) +{ + if (all(equal(gl_LocalInvocationID, uvec3(0)))) { + shared_max_slight_focus_abs_coc = floatBitsToUint(0.0); + } + barrier(); + + float local_abs_max = 0.0; + /* Sample in a cross (X) pattern. This covers all pixels over the whole tile, as long as + * dof_max_slight_focus_radius is less than the group size. */ + for (int i = 0; i < 4; i++) { + vec2 sample_uv = (frag_coord + quad_offsets[i] * 2.0 * dof_max_slight_focus_radius) / + vec2(textureSize(color_tx, 0)); + float coc = dof_coc_from_depth(dof_buf, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r); + coc = clamp(coc, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); + if (abs(coc) < dof_max_slight_focus_radius) { + local_abs_max = max(local_abs_max, abs(coc)); + } + } + /* Use atomic reduce operation. */ + atomicMax(shared_max_slight_focus_abs_coc, floatBitsToUint(local_abs_max)); + /* "Broadcast" result across all threads. */ + barrier(); + + return uintBitsToFloat(shared_max_slight_focus_abs_coc); +} + +vec3 dof_neighborhood_clamp(vec2 frag_coord, vec3 color, float center_coc, float weight) +{ + /* Stabilize color by clamping with the stable half res neighborhood. */ + vec3 neighbor_min, neighbor_max; + const vec2 corners[4] = vec2[4](vec2(-1, -1), vec2(1, -1), vec2(-1, 1), vec2(1, 1)); + for (int i = 0; i < 4; i++) { + /** + * Visit the 4 half-res texels around (and containing) the fullres texel. + * Here a diagram of a fullscreen texel (f) in the bottom left corner of a half res texel. + * We sample the stable half-resolution texture at the 4 location denoted by (h). + * ┌───────┬───────┐ + * │ h │ h │ + * │ │ │ + * │ │ f │ + * ├───────┼───────┤ + * │ h │ h │ + * │ │ │ + * │ │ │ + * └───────┴───────┘ + */ + vec2 uv_sample = ((frag_coord + corners[i]) * 0.5) / vec2(textureSize(stable_color_tx, 0)); + /* Reminder: The content of this buffer is YCoCg + CoC. */ + vec3 ycocg_sample = textureLod(stable_color_tx, uv_sample, 0.0).rgb; + neighbor_min = (i == 0) ? ycocg_sample : min(neighbor_min, ycocg_sample); + neighbor_max = (i == 0) ? ycocg_sample : max(neighbor_max, ycocg_sample); + } + /* Pad the bounds in the near in focus region to get back a bit of detail. */ + float padding = 0.125 * saturate(1.0 - sqr(center_coc) / sqr(8.0)); + neighbor_max += abs(neighbor_min) * padding; + neighbor_min -= abs(neighbor_min) * padding; + /* Progressively apply the clamp to avoid harsh transition. Also mask by weight. */ + float fac = saturate(sqr(center_coc) * 4.0) * weight; + /* Clamp in YCoCg space to avoid too much color drift. */ + color = colorspace_YCoCg_from_scene_linear(color); + color = mix(color, clamp(color, neighbor_min, neighbor_max), fac); + color = colorspace_scene_linear_from_YCoCg(color); + return color; +} + +void main() +{ + vec2 frag_coord = vec2(gl_GlobalInvocationID.xy) + 0.5; + ivec2 tile_co = ivec2(frag_coord / float(DOF_TILES_SIZE * 2)); + + CocTile coc_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, tile_co); + CocTilePrediction prediction = dof_coc_tile_prediction_get(coc_tile); + + vec2 uv = frag_coord / vec2(textureSize(color_tx, 0)); + vec2 uv_halfres = (frag_coord * 0.5) / vec2(textureSize(color_bg_tx, 0)); + + float slight_focus_max_coc = 0.0; + if (prediction.do_slight_focus) { + slight_focus_max_coc = dof_slight_focus_coc_tile_get(frag_coord); + prediction.do_slight_focus = slight_focus_max_coc >= 0.5; + if (prediction.do_slight_focus) { + prediction.do_focus = false; + } + } + + if (prediction.do_focus) { + float center_coc = (dof_coc_from_depth(dof_buf, uv, textureLod(depth_tx, uv, 0.0).r)); + prediction.do_focus = abs(center_coc) <= 0.5; + } + + vec4 out_color = vec4(0.0); + float weight = 0.0; + + vec4 layer_color; + float layer_weight; + + if (!no_hole_fill_pass && prediction.do_hole_fill) { + layer_color = textureLod(color_hole_fill_tx, uv_halfres, 0.0); + layer_weight = textureLod(weight_hole_fill_tx, uv_halfres, 0.0).r; + out_color = layer_color * safe_rcp(layer_weight); + weight = float(layer_weight > 0.0); + } + + if (!no_background_pass && prediction.do_background) { + layer_color = textureLod(color_bg_tx, uv_halfres, 0.0); + layer_weight = textureLod(weight_bg_tx, uv_halfres, 0.0).r; + /* Always prefer background to hole_fill pass. */ + layer_color *= safe_rcp(layer_weight); + layer_weight = float(layer_weight > 0.0); + /* Composite background. */ + out_color = out_color * (1.0 - layer_weight) + layer_color; + weight = weight * (1.0 - layer_weight) + layer_weight; + /* Fill holes with the composited background. */ + out_color *= safe_rcp(weight); + weight = float(weight > 0.0); + } + + if (!no_slight_focus_pass && prediction.do_slight_focus) { + float center_coc; + dof_slight_focus_gather(depth_tx, + color_tx, + bokeh_lut_tx, + slight_focus_max_coc, + layer_color, + layer_weight, + center_coc); + + /* Composite slight defocus. */ + out_color = out_color * (1.0 - layer_weight) + layer_color; + weight = weight * (1.0 - layer_weight) + layer_weight; + + out_color.rgb = dof_neighborhood_clamp(frag_coord, out_color.rgb, center_coc, layer_weight); + } + + if (!no_focus_pass && prediction.do_focus) { + layer_color = safe_color(textureLod(color_tx, uv, 0.0)); + layer_weight = 1.0; + /* Composite in focus. */ + out_color = out_color * (1.0 - layer_weight) + layer_color; + weight = weight * (1.0 - layer_weight) + layer_weight; + } + + if (!no_foreground_pass && prediction.do_foreground) { + layer_color = textureLod(color_fg_tx, uv_halfres, 0.0); + layer_weight = textureLod(weight_fg_tx, uv_halfres, 0.0).r; + /* Composite foreground. */ + out_color = out_color * (1.0 - layer_weight) + layer_color; + } + + /* Fix float precision issue in alpha compositing. */ + if (out_color.a > 0.99) { + out_color.a = 1.0; + } + + if (debug_resolve_perf && prediction.do_slight_focus) { + out_color.rgb *= vec3(1.0, 0.1, 0.1); + } + + imageStore(out_color_img, ivec2(gl_GlobalInvocationID.xy), out_color); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl new file mode 100644 index 00000000000..cfb7fd2568b --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_frag.glsl @@ -0,0 +1,62 @@ + +/** + * Scatter pass: Use sprites to scatter the color of very bright pixel to have higher quality blur. + * + * We only scatter one quad per sprite and one sprite per 4 pixels to reduce vertex shader + * invocations and overdraw. + */ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +#define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0)) + +void main() +{ + vec4 coc4 = vec4(interp.color_and_coc1.w, + interp.color_and_coc2.w, + interp.color_and_coc3.w, + interp.color_and_coc4.w); + vec4 shapes; + if (use_bokeh_lut) { + shapes = vec4(texture(bokeh_lut_tx, interp.rect_uv1).r, + texture(bokeh_lut_tx, interp.rect_uv2).r, + texture(bokeh_lut_tx, interp.rect_uv3).r, + texture(bokeh_lut_tx, interp.rect_uv4).r); + } + else { + shapes = vec4(length(interp.rect_uv1), + length(interp.rect_uv2), + length(interp.rect_uv3), + length(interp.rect_uv4)); + } + shapes *= interp.distance_scale; + /* Becomes signed distance field in pixel units. */ + shapes -= coc4; + /* Smooth the edges a bit to fade out the undersampling artifacts. */ + shapes = saturate(1.0 - linearstep(-0.8, 0.8, shapes)); + /* Outside of bokeh shape. Try to avoid overloading ROPs. */ + if (max_v4(shapes) == 0.0) { + discard; + } + + if (!no_scatter_occlusion) { + /* Works because target is the same size as occlusion_tx. */ + vec2 uv = gl_FragCoord.xy / vec2(textureSize(occlusion_tx, 0).xy); + vec2 occlusion_data = texture(occlusion_tx, uv).rg; + /* Fix tilling artifacts. (Slide 90) */ + const float correction_fac = 1.0 - DOF_FAST_GATHER_COC_ERROR; + /* Occlude the sprite with geometry from the same field using a chebychev test (slide 85). */ + float mean = occlusion_data.x; + float variance = occlusion_data.y; + shapes *= variance * safe_rcp(variance + sqr(max(coc4 * correction_fac - mean, 0.0))); + } + + out_color = (interp.color_and_coc1 * shapes[0] + interp.color_and_coc2 * shapes[1] + + interp.color_and_coc3 * shapes[2] + interp.color_and_coc4 * shapes[3]); + /* Do not accumulate alpha. This has already been accumulated by the gather pass. */ + out_color.a = 0.0; + + if (debug_scatter_perf) { + out_color.rgb = avg(out_color.rgb) * vec3(1.0, 0.0, 0.0); + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl new file mode 100644 index 00000000000..d870496a06c --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_scatter_vert.glsl @@ -0,0 +1,45 @@ + +/** + * Scatter pass: Use sprites to scatter the color of very bright pixel to have higher quality blur. + * + * We only scatter one triangle per sprite and one sprite per 4 pixels to reduce vertex shader + * invocations and overdraw. + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +void main() +{ + ScatterRect rect = scatter_list_buf[gl_InstanceID]; + + interp.color_and_coc1 = rect.color_and_coc[0]; + interp.color_and_coc2 = rect.color_and_coc[1]; + interp.color_and_coc3 = rect.color_and_coc[2]; + interp.color_and_coc4 = rect.color_and_coc[3]; + + vec2 uv = vec2(gl_VertexID & 1, gl_VertexID >> 1) * 2.0 - 1.0; + uv = uv * rect.half_extent; + + gl_Position = vec4(uv + rect.offset, 0.0, 1.0); + /* NDC range [-1..1]. */ + gl_Position.xy = (gl_Position.xy / vec2(textureSize(occlusion_tx, 0).xy)) * 2.0 - 1.0; + + if (use_bokeh_lut) { + /* Bias scale to avoid sampling at the texture's border. */ + interp.distance_scale = (float(DOF_BOKEH_LUT_SIZE) / float(DOF_BOKEH_LUT_SIZE - 1)); + vec2 uv_div = 1.0 / (interp.distance_scale * abs(rect.half_extent)); + interp.rect_uv1 = ((uv + quad_offsets[0]) * uv_div) * 0.5 + 0.5; + interp.rect_uv2 = ((uv + quad_offsets[1]) * uv_div) * 0.5 + 0.5; + interp.rect_uv3 = ((uv + quad_offsets[2]) * uv_div) * 0.5 + 0.5; + interp.rect_uv4 = ((uv + quad_offsets[3]) * uv_div) * 0.5 + 0.5; + /* Only for sampling. */ + interp.distance_scale *= max_v2(abs(rect.half_extent)); + } + else { + interp.distance_scale = 1.0; + interp.rect_uv1 = uv + quad_offsets[0]; + interp.rect_uv2 = uv + quad_offsets[1]; + interp.rect_uv3 = uv + quad_offsets[2]; + interp.rect_uv4 = uv + quad_offsets[3]; + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl new file mode 100644 index 00000000000..c017a5aa965 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_setup_comp.glsl @@ -0,0 +1,46 @@ + +/** + * Setup pass: CoC and luma aware downsample to half resolution of the input scene color buffer. + * + * An addition to the downsample CoC, we output the maximum slight out of focus CoC to be + * sure we don't miss a pixel. + * + * Input: + * Full-resolution color & depth buffer + * Output: + * Half-resolution Color, signed CoC (out_coc.x), and max slight focus abs CoC (out_coc.y). + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +void main() +{ + vec2 fullres_texel_size = 1.0 / vec2(textureSize(color_tx, 0).xy); + /* Center uv around the 4 fullres pixels. */ + vec2 quad_center = vec2(gl_GlobalInvocationID.xy * 2 + 1) * fullres_texel_size; + + vec4 colors[4]; + vec4 cocs; + for (int i = 0; i < 4; i++) { + vec2 sample_uv = quad_center + quad_offsets[i] * fullres_texel_size; + /* NOTE: We use samplers without filtering. */ + colors[i] = safe_color(textureLod(color_tx, sample_uv, 0.0)); + cocs[i] = dof_coc_from_depth(dof_buf, sample_uv, textureLod(depth_tx, sample_uv, 0.0).r); + } + + cocs = clamp(cocs, -dof_buf.coc_abs_max, dof_buf.coc_abs_max); + + vec4 weights = dof_bilateral_coc_weights(cocs); + weights *= dof_bilateral_color_weights(colors); + /* Normalize so that the sum is 1. */ + weights *= safe_rcp(sum(weights)); + + ivec2 out_texel = ivec2(gl_GlobalInvocationID.xy); + vec4 out_color = weighted_sum_array(colors, weights); + imageStore(out_color_img, out_texel, out_color); + + float out_coc = dot(cocs, weights); + imageStore(out_coc_img, out_texel, vec4(out_coc)); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl new file mode 100644 index 00000000000..5ffedf3068b --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_stabilize_comp.glsl @@ -0,0 +1,367 @@ + +/** + * Temporal Stabilization of the Depth of field input. + * Corresponds to the TAA pass in the paper. + * We actually duplicate the TAA logic but with a few changes: + * - We run this pass at half resolution. + * - We store CoC instead of Opacity in the alpha channel of the history. + * + * This is and adaption of the code found in eevee_film_lib.glsl + * + * Inputs: + * - Output of setup pass (halfres). + * Outputs: + * - Stabilized Color and CoC (halfres). + **/ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) + +struct DofSample { + vec4 color; + float coc; +}; + +/* -------------------------------------------------------------------- */ +/** \name LDS Cache + * \{ */ + +const uint cache_size = gl_WorkGroupSize.x + 2; +shared vec4 color_cache[cache_size][cache_size]; +shared float coc_cache[cache_size][cache_size]; +/* Need 2 pixel border for depth. */ +const uint cache_depth_size = gl_WorkGroupSize.x + 4; +shared float depth_cache[cache_depth_size][cache_depth_size]; + +void dof_cache_init() +{ + /** + * Load enough values into LDS to perform the filter. + * + * ┌──────────────────────────────┐ + * │ │ < Border texels that needs to be loaded. + * │ x x x x x x x x │ ─┐ + * │ x x x x x x x x │ │ + * │ x x x x x x x x │ │ + * │ x x x x x x x x │ │ Thread Group Size 8x8. + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ │ + * │ L L L L L x x x x │ ─┘ + * │ L L L L L │ < Border texels that needs to be loaded. + * └──────────────────────────────┘ + * └───────────┘ + * Load using 5x5 threads. + */ + + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + for (int y = 0; y < 2; y++) { + for (int x = 0; x < 2; x++) { + /* 1 Pixel border. */ + if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_size / 2u)))) { + ivec2 offset = ivec2(x, y) * ivec2(cache_size / 2u); + ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset; + ivec2 load_texel = clamp(texel + offset - 1, ivec2(0), textureSize(color_tx, 0) - 1); + + vec4 color = texelFetch(color_tx, load_texel, 0); + color_cache[cache_texel.y][cache_texel.x] = colorspace_YCoCg_from_scene_linear(color); + coc_cache[cache_texel.y][cache_texel.x] = texelFetch(coc_tx, load_texel, 0).x; + } + /* 2 Pixels border. */ + if (all(lessThan(gl_LocalInvocationID.xy, uvec2(cache_depth_size / 2u)))) { + ivec2 offset = ivec2(x, y) * ivec2(cache_depth_size / 2u); + ivec2 cache_texel = ivec2(gl_LocalInvocationID.xy) + offset; + /* Depth is fullres. Load every 2 pixels. */ + ivec2 load_texel = clamp((texel + offset - 2) * 2, ivec2(0), textureSize(depth_tx, 0) - 1); + + depth_cache[cache_texel.y][cache_texel.x] = texelFetch(depth_tx, load_texel, 0).x; + } + } + } + barrier(); +} + +/* Note: Sample color space is already in YCoCg space. */ +DofSample dof_fetch_input_sample(ivec2 offset) +{ + ivec2 coord = offset + 1 + ivec2(gl_LocalInvocationID.xy); + return DofSample(color_cache[coord.y][coord.x], coc_cache[coord.y][coord.x]); +} + +float dof_fetch_half_depth(ivec2 offset) +{ + ivec2 coord = offset + 2 + ivec2(gl_LocalInvocationID.xy); + return depth_cache[coord.y][coord.x]; +} + +/** \} */ + +float dof_luma_weight(float luma) +{ + /* Slide 20 of "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014. */ + /* To preserve more details in dark areas, we use a bigger bias. */ + const float exposure_scale = 1.0; /* TODO. */ + return 1.0 / (4.0 + luma * exposure_scale); +} + +float dof_bilateral_weight(float reference_coc, float sample_coc) +{ + /* NOTE: The difference between the cocs should be inside a abs() function, + * but we follow UE4 implementation to improve how dithered transparency looks (see slide 19). + * Effectively bleed background into foreground. + * Compared to dof_bilateral_coc_weights() this saturates as 2x the reference CoC. */ + return saturate(1.0 - (sample_coc - reference_coc) / max(1.0, abs(reference_coc))); +} + +DofSample dof_spatial_filtering() +{ + /* Plus (+) shape offsets. */ + const ivec2 plus_offsets[4] = ivec2[4](ivec2(-1, 0), ivec2(0, -1), ivec2(1, 0), ivec2(0, 1)); + DofSample center = dof_fetch_input_sample(ivec2(0)); + DofSample accum = DofSample(vec4(0.0), 0.0); + float accum_weight = 0.0; + for (int i = 0; i < 4; i++) { + DofSample samp = dof_fetch_input_sample(plus_offsets[i]); + float weight = dof_buf.filter_samples_weight[i] * dof_luma_weight(samp.color.x) * + dof_bilateral_weight(center.coc, samp.coc); + + accum.color += samp.color * weight; + accum.coc += samp.coc * weight; + accum_weight += weight; + } + /* Accumulate center sample last as it does not need bilateral_weights. */ + float weight = dof_buf.filter_center_weight * dof_luma_weight(center.color.x); + accum.color += center.color * weight; + accum.coc += center.coc * weight; + accum_weight += weight; + + float rcp_weight = 1.0 / accum_weight; + accum.color *= rcp_weight; + accum.coc *= rcp_weight; + return accum; +} + +struct DofNeighborhoodMinMax { + DofSample min; + DofSample max; +}; + +/* Return history clipping bounding box in YCoCg color space. */ +DofNeighborhoodMinMax dof_neighbor_boundbox() +{ + /* Plus (+) shape offsets. */ + const ivec2 plus_offsets[4] = ivec2[4](ivec2(-1, 0), ivec2(0, -1), ivec2(1, 0), ivec2(0, 1)); + /** + * Simple bounding box calculation in YCoCg as described in: + * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 + */ + DofSample min_c = dof_fetch_input_sample(ivec2(0)); + DofSample max_c = min_c; + for (int i = 0; i < 4; i++) { + DofSample samp = dof_fetch_input_sample(plus_offsets[i]); + min_c.color = min(min_c.color, samp.color); + max_c.color = max(max_c.color, samp.color); + min_c.coc = min(min_c.coc, samp.coc); + max_c.coc = max(max_c.coc, samp.coc); + } + /* (Slide 32) Simple clamp to min/max of 8 neighbors results in 3x3 box artifacts. + * Round bbox shape by averaging 2 different min/max from 2 different neighborhood. */ + DofSample min_c_3x3 = min_c; + DofSample max_c_3x3 = max_c; + const ivec2 corners[4] = ivec2[4](ivec2(-1, -1), ivec2(1, -1), ivec2(-1, 1), ivec2(1, 1)); + for (int i = 0; i < 4; i++) { + DofSample samp = dof_fetch_input_sample(corners[i]); + min_c_3x3.color = min(min_c_3x3.color, samp.color); + max_c_3x3.color = max(max_c_3x3.color, samp.color); + min_c_3x3.coc = min(min_c_3x3.coc, samp.coc); + max_c_3x3.coc = max(max_c_3x3.coc, samp.coc); + } + min_c.color = (min_c.color + min_c_3x3.color) * 0.5; + max_c.color = (max_c.color + max_c_3x3.color) * 0.5; + min_c.coc = (min_c.coc + min_c_3x3.coc) * 0.5; + max_c.coc = (max_c.coc + max_c_3x3.coc) * 0.5; + + return DofNeighborhoodMinMax(min_c, max_c); +} + +/* Returns motion in pixel space to retrieve the pixel history. */ +vec2 dof_pixel_history_motion_vector(ivec2 texel_sample) +{ + /** + * Dilate velocity by using the nearest pixel in a cross pattern. + * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 27) + */ + const ivec2 corners[4] = ivec2[4](ivec2(-2, -2), ivec2(2, -2), ivec2(-2, 2), ivec2(2, 2)); + float min_depth = dof_fetch_half_depth(ivec2(0)); + ivec2 nearest_texel = ivec2(0); + for (int i = 0; i < 4; i++) { + float depth = dof_fetch_half_depth(corners[i]); + if (min_depth > depth) { + min_depth = depth; + nearest_texel = corners[i]; + } + } + /* Convert to full resolution buffer pixel. */ + ivec2 velocity_texel = (texel_sample + nearest_texel) * 2; + velocity_texel = clamp(velocity_texel, ivec2(0), textureSize(velocity_tx, 0).xy - 1); + vec4 vector = velocity_resolve(velocity_tx, velocity_texel, min_depth); + /* Transform to **half** pixel space. */ + return vector.xy * vec2(textureSize(color_tx, 0)); +} + +/* Load color using a special filter to avoid losing detail. + * \a texel is sample position with subpixel accuracy. */ +DofSample dof_sample_history(vec2 input_texel) +{ +#if 1 /* Bilinar. */ + vec2 uv = vec2(input_texel + 0.5) / textureSize(in_history_tx, 0); + vec4 color = textureLod(in_history_tx, uv, 0.0); + +#else /* Catmull Rom interpolation. 5 Bilinear Taps. */ + vec2 center_texel; + vec2 inter_texel = modf(input_texel, center_texel); + vec2 weights[4]; + film_get_catmull_rom_weights(inter_texel, weights); + + /** + * Use optimized version by leveraging bilinear filtering from hardware sampler and by removing + * corner taps. + * From "Filmic SMAA" by Jorge Jimenez at Siggraph 2016 + * http://advances.realtimerendering.com/s2016/Filmic%20SMAA%20v7.pptx + */ + center_texel += 0.5; + + /* Slide 92. */ + vec2 weight_12 = weights[1] + weights[2]; + vec2 uv_12 = (center_texel + weights[2] / weight_12) * film_buf.extent_inv; + vec2 uv_0 = (center_texel - 1.0) * film_buf.extent_inv; + vec2 uv_3 = (center_texel + 2.0) * film_buf.extent_inv; + + vec4 color; + vec4 weight_cross = weight_12.xyyx * vec4(weights[0].yx, weights[3].xy); + float weight_center = weight_12.x * weight_12.y; + + color = textureLod(in_history_tx, uv_12, 0.0) * weight_center; + color += textureLod(in_history_tx, vec2(uv_12.x, uv_0.y), 0.0) * weight_cross.x; + color += textureLod(in_history_tx, vec2(uv_0.x, uv_12.y), 0.0) * weight_cross.y; + color += textureLod(in_history_tx, vec2(uv_3.x, uv_12.y), 0.0) * weight_cross.z; + color += textureLod(in_history_tx, vec2(uv_12.x, uv_3.y), 0.0) * weight_cross.w; + /* Re-normalize for the removed corners. */ + color /= (weight_center + sum(weight_cross)); +#endif + /* NOTE(fclem): Opacity is wrong on purpose. Final Opacity does not rely on history. */ + return DofSample(color.xyzz, color.w); +} + +/* Modulate the history color to avoid ghosting artifact. */ +DofSample dof_amend_history(DofNeighborhoodMinMax bbox, DofSample history, DofSample src) +{ +#if 0 + /* Clip instead of clamping to avoid color accumulating in the AABB corners. */ + vec3 clip_dir = src.color.rgb - history.color.rgb; + + float t = line_aabb_clipping_dist( + history.color.rgb, clip_dir, bbox.min.color.rgb, bbox.max.color.rgb); + history.color.rgb += clip_dir * saturate(t); +#else + /* More responsive. */ + history.color = clamp(history.color, bbox.min.color, bbox.max.color); +#endif + /* Clamp CoC to reduce convergence time. Otherwise the result is laggy. */ + history.coc = clamp(history.coc, bbox.min.coc, bbox.max.coc); + + return history; +} + +float dof_history_blend_factor( + float velocity, vec2 texel, DofNeighborhoodMinMax bbox, DofSample src, DofSample dst) +{ + float luma_min = bbox.min.color.x; + float luma_max = bbox.max.color.x; + float luma_incoming = src.color.x; + float luma_history = dst.color.x; + + /* 5% of incoming color by default. */ + float blend = 0.05; + /* Blend less history if the pixel has substantial velocity. */ + /* NOTE(fclem): velocity threshold multiplied by 2 because of half resolution. */ + blend = mix(blend, 0.20, saturate(velocity * 0.02 * 2.0)); + /** + * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 43) + * Bias towards history if incoming pixel is near clamping. Reduces flicker. + */ + float distance_to_luma_clip = min_v2(vec2(luma_history - luma_min, luma_max - luma_history)); + /* Divide by bbox size to get a factor. 2 factor to compensate the line above. */ + distance_to_luma_clip *= 2.0 * safe_rcp(luma_max - luma_min); + /* Linearly blend when history gets below to 25% of the bbox size. */ + blend *= saturate(distance_to_luma_clip * 4.0 + 0.1); + /* Progressively discard history until history CoC is twice as big as the filtered CoC. + * Note we use absolute diff here because we are not comparing neighbors and thus do not risk to + * dilate thin features like hair (slide 19). */ + float coc_diff_ratio = saturate(abs(src.coc - dst.coc) / max(1.0, abs(src.coc))); + blend = mix(blend, 1.0, coc_diff_ratio); + /* Discard out of view history. */ + if (any(lessThan(texel, vec2(0))) || + any(greaterThanEqual(texel, vec2(imageSize(out_history_img))))) { + blend = 1.0; + } + /* Discard history if invalid. */ + if (use_history == false) { + blend = 1.0; + } + return blend; +} + +void main() +{ + dof_cache_init(); + + ivec2 src_texel = ivec2(gl_GlobalInvocationID.xy); + + /** + * Naming convention is taken from the film implementation. + * SRC is incoming new data. + * DST is history data. + */ + DofSample src = dof_spatial_filtering(); + + /* Reproject by finding where this pixel was in the previous frame. */ + vec2 motion = dof_pixel_history_motion_vector(src_texel); + vec2 history_texel = vec2(src_texel) + motion; + + float velocity = length(motion); + + DofSample dst = dof_sample_history(history_texel); + + /* Get local color bounding box of source neighborhood. */ + DofNeighborhoodMinMax bbox = dof_neighbor_boundbox(); + + float blend = dof_history_blend_factor(velocity, history_texel, bbox, src, dst); + + dst = dof_amend_history(bbox, dst, src); + + /* Luma weighted blend to reduce flickering. */ + float weight_dst = dof_luma_weight(dst.color.x) * (1.0 - blend); + float weight_src = dof_luma_weight(src.color.x) * (blend); + + DofSample result; + /* Weighted blend. */ + result.color = vec4(dst.color.rgb, dst.coc) * weight_dst + + vec4(src.color.rgb, src.coc) * weight_src; + result.color /= weight_src + weight_dst; + + /* Save history for next iteration. Still in YCoCg space with CoC in alpha. */ + imageStore(out_history_img, src_texel, result.color); + + /* Un-swizzle. */ + result.coc = result.color.a; + /* Clamp opacity since we don't store it in history. */ + result.color.a = clamp(src.color.a, bbox.min.color.a, bbox.max.color.a); + + result.color = colorspace_scene_linear_from_YCoCg(result.color); + + imageStore(out_color_img, src_texel, result.color); + imageStore(out_coc_img, src_texel, vec4(result.coc)); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl new file mode 100644 index 00000000000..dba8b5fd79d --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_dilate_comp.glsl @@ -0,0 +1,97 @@ + +/** + * Tile dilate pass: Takes the 8x8 Tiles buffer and converts dilates the tiles with large CoC to + * their neighborhood. This pass is repeated multiple time until the maximum CoC can be covered. + * + * Input & Output: + * - Separated foreground and background CoC. 1/8th of half-res resolution. So 1/16th of full-res. + **/ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +/* Error introduced by the random offset of the gathering kernel's center. */ +const float bluring_radius_error = 1.0 + 1.0 / (float(DOF_GATHER_RING_COUNT) + 0.5); +const float tile_to_fullres_factor = float(DOF_TILES_SIZE * 2); + +void main() +{ + ivec2 center_tile_pos = ivec2(gl_GlobalInvocationID.xy); + + CocTile ring_buckets[DOF_DILATE_RING_COUNT]; + + for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) { + ring_buckets[ring] = dof_coc_tile_init(); + + int ring_distance = ring + 1; + for (int sample_id = 0; sample_id < 4 * ring_distance; sample_id++) { + ivec2 offset = dof_square_ring_sample_offset(ring_distance, sample_id); + + offset *= ring_width_multiplier; + + for (int i = 0; i < 2; i++) { + ivec2 adj_tile_pos = center_tile_pos + ((i == 0) ? offset : -offset); + + CocTile adj_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, adj_tile_pos); + + if (DILATE_MODE_MIN_MAX) { + /* Actually gather the "absolute" biggest coc but keeping the sign. */ + ring_buckets[ring].fg_min_coc = min(ring_buckets[ring].fg_min_coc, adj_tile.fg_min_coc); + ring_buckets[ring].bg_max_coc = max(ring_buckets[ring].bg_max_coc, adj_tile.bg_max_coc); + } + else { /* DILATE_MODE_MIN_ABS */ + ring_buckets[ring].fg_max_coc = max(ring_buckets[ring].fg_max_coc, adj_tile.fg_max_coc); + ring_buckets[ring].bg_min_coc = min(ring_buckets[ring].bg_min_coc, adj_tile.bg_min_coc); + + /* Should be tight as possible to reduce gather overhead (see slide 61). */ + float closest_neighbor_distance = length(max(abs(vec2(offset)) - 1.0, 0.0)) * + tile_to_fullres_factor; + + ring_buckets[ring].fg_max_intersectable_coc = max( + ring_buckets[ring].fg_max_intersectable_coc, + adj_tile.fg_max_intersectable_coc + closest_neighbor_distance); + ring_buckets[ring].bg_min_intersectable_coc = min( + ring_buckets[ring].bg_min_intersectable_coc, + adj_tile.bg_min_intersectable_coc + closest_neighbor_distance); + } + } + } + } + + /* Load center tile. */ + CocTile out_tile = dof_coc_tile_load(in_tiles_fg_img, in_tiles_bg_img, center_tile_pos); + + for (int ring = 0; ring < ring_count && ring < DOF_DILATE_RING_COUNT; ring++) { + float ring_distance = float(ring + 1); + + ring_distance = (ring_distance * ring_width_multiplier - 1) * tile_to_fullres_factor; + + if (DILATE_MODE_MIN_MAX) { + /* NOTE(fclem): Unsure if both sides of the inequalities have the same unit. */ + if (-ring_buckets[ring].fg_min_coc * bluring_radius_error > ring_distance) { + out_tile.fg_min_coc = min(out_tile.fg_min_coc, ring_buckets[ring].fg_min_coc); + } + + if (ring_buckets[ring].bg_max_coc * bluring_radius_error > ring_distance) { + out_tile.bg_max_coc = max(out_tile.bg_max_coc, ring_buckets[ring].bg_max_coc); + } + } + else { /* DILATE_MODE_MIN_ABS */ + /* Find minimum absolute CoC radii that will be intersected for the previously + * computed maximum CoC values. */ + if (-out_tile.fg_min_coc * bluring_radius_error > ring_distance) { + out_tile.fg_max_coc = max(out_tile.fg_max_coc, ring_buckets[ring].fg_max_coc); + out_tile.fg_max_intersectable_coc = max(out_tile.fg_max_intersectable_coc, + ring_buckets[ring].fg_max_intersectable_coc); + } + + if (out_tile.bg_max_coc * bluring_radius_error > ring_distance) { + out_tile.bg_min_coc = min(out_tile.bg_min_coc, ring_buckets[ring].bg_min_coc); + out_tile.bg_min_intersectable_coc = min(out_tile.bg_min_intersectable_coc, + ring_buckets[ring].bg_min_intersectable_coc); + } + } + } + + ivec2 texel_out = ivec2(gl_GlobalInvocationID.xy); + dof_coc_tile_store(out_tiles_fg_img, out_tiles_bg_img, texel_out, out_tile); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl new file mode 100644 index 00000000000..88737ade386 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_depth_of_field_tiles_flatten_comp.glsl @@ -0,0 +1,78 @@ + +/** + * Tile flatten pass: Takes the halfres CoC buffer and converts it to 8x8 tiles. + * + * Output min and max values for each tile and for both foreground & background. + * Also outputs min intersectable CoC for the background, which is the minimum CoC + * that comes from the background pixels. + * + * Input: + * - Half-resolution Circle of confusion. Out of setup pass. + * Output: + * - Separated foreground and background CoC. 1/8th of half-res resolution. So 1/16th of full-res. + */ + +#pragma BLENDER_REQUIRE(eevee_depth_of_field_lib.glsl) + +/** + * In order to use atomic operations, we have to use uints. But this means having to deal with the + * negative number ourselves. Luckily, each ground have a nicely defined range of values we can + * remap to positive float. + */ +shared uint fg_min_coc; +shared uint fg_max_coc; +shared uint fg_max_intersectable_coc; +shared uint bg_min_coc; +shared uint bg_max_coc; +shared uint bg_min_intersectable_coc; + +const uint dof_tile_large_coc_uint = floatBitsToUint(dof_tile_large_coc); + +void main() +{ + if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) { + /* NOTE: Min/Max flipped because of inverted fg_coc sign. */ + fg_min_coc = floatBitsToUint(0.0); + fg_max_coc = dof_tile_large_coc_uint; + fg_max_intersectable_coc = dof_tile_large_coc_uint; + bg_min_coc = dof_tile_large_coc_uint; + bg_max_coc = floatBitsToUint(0.0); + bg_min_intersectable_coc = dof_tile_large_coc_uint; + } + barrier(); + + ivec2 sample_texel = min(ivec2(gl_GlobalInvocationID.xy), textureSize(coc_tx, 0).xy - 1); + vec2 sample_data = texelFetch(coc_tx, sample_texel, 0).rg; + + float sample_coc = sample_data.x; + uint fg_coc = floatBitsToUint(max(-sample_coc, 0.0)); + /* NOTE: atomicMin/Max flipped because of inverted fg_coc sign. */ + atomicMax(fg_min_coc, fg_coc); + atomicMin(fg_max_coc, fg_coc); + atomicMin(fg_max_intersectable_coc, (sample_coc < 0.0) ? fg_coc : dof_tile_large_coc_uint); + + uint bg_coc = floatBitsToUint(max(sample_coc, 0.0)); + atomicMin(bg_min_coc, bg_coc); + atomicMax(bg_max_coc, bg_coc); + atomicMin(bg_min_intersectable_coc, (sample_coc > 0.0) ? bg_coc : dof_tile_large_coc_uint); + + barrier(); + + if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) { + if (fg_max_intersectable_coc == dof_tile_large_coc_uint) { + fg_max_intersectable_coc = floatBitsToUint(0.0); + } + + CocTile tile; + /* Foreground sign is flipped since we compare unsigned representation. */ + tile.fg_min_coc = -uintBitsToFloat(fg_min_coc); + tile.fg_max_coc = -uintBitsToFloat(fg_max_coc); + tile.fg_max_intersectable_coc = -uintBitsToFloat(fg_max_intersectable_coc); + tile.bg_min_coc = uintBitsToFloat(bg_min_coc); + tile.bg_max_coc = uintBitsToFloat(bg_max_coc); + tile.bg_min_intersectable_coc = uintBitsToFloat(bg_min_intersectable_coc); + + ivec2 tile_co = ivec2(gl_WorkGroupID.xy); + dof_coc_tile_store(out_tiles_fg_img, out_tiles_bg_img, tile_co, tile); + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl index b286836e8df..bf6293d5561 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_film_lib.glsl @@ -7,6 +7,7 @@ #pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) #pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) #pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_colorspace_lib.glsl) /* Return scene linear Z depth from the camera or radial depth for panoramic cameras. */ float film_depth_convert_to_scene(float depth) @@ -18,32 +19,6 @@ float film_depth_convert_to_scene(float depth) return abs(get_view_z_from_depth(depth)); } -vec3 film_YCoCg_from_scene_linear(vec3 rgb_color) -{ - const mat3 colorspace_tx = transpose(mat3(vec3(1, 2, 1), /* Y */ - vec3(2, 0, -2), /* Co */ - vec3(-1, 2, -1))); /* Cg */ - return colorspace_tx * rgb_color; -} - -vec4 film_YCoCg_from_scene_linear(vec4 rgba_color) -{ - return vec4(film_YCoCg_from_scene_linear(rgba_color.rgb), rgba_color.a); -} - -vec3 film_scene_linear_from_YCoCg(vec3 ycocg_color) -{ - float Y = ycocg_color.x; - float Co = ycocg_color.y; - float Cg = ycocg_color.z; - - vec3 rgb_color; - rgb_color.r = Y + Co - Cg; - rgb_color.g = Y + Cg; - rgb_color.b = Y - Co - Cg; - return rgb_color * 0.25; -} - /* Load a texture sample in a specific format. Combined pass needs to use this. */ vec4 film_texelfetch_as_YCoCg_opacity(sampler2D tx, ivec2 texel) { @@ -51,7 +26,7 @@ vec4 film_texelfetch_as_YCoCg_opacity(sampler2D tx, ivec2 texel) /* Convert transmittance to opacity. */ color.a = saturate(1.0 - color.a); /* Transform to YCoCg for accumulation. */ - color.rgb = film_YCoCg_from_scene_linear(color.rgb); + color.rgb = colorspace_YCoCg_from_scene_linear(color.rgb); return color; } @@ -220,7 +195,7 @@ vec2 film_pixel_history_motion_vector(ivec2 texel_sample) float min_depth = texelFetch(depth_tx, texel_sample, 0).x; ivec2 nearest_texel = texel_sample; for (int i = 0; i < 4; i++) { - ivec2 texel = clamp(texel_sample + corners[i], ivec2(0), textureSize(depth_tx, 0).xy); + ivec2 texel = clamp(texel_sample + corners[i], ivec2(0), textureSize(depth_tx, 0).xy - 1); float depth = texelFetch(depth_tx, texel, 0).x; if (min_depth > depth) { min_depth = depth; @@ -254,7 +229,7 @@ void film_get_catmull_rom_weights(vec2 t, out vec2 weights[4]) weights[3] = fct3 - fct2; } -/* Load color using a special filter to avoid loosing detail. +/* Load color using a special filter to avoid losing detail. * \a texel is sample position with subpixel accuracy. */ vec4 film_sample_catmull_rom(sampler2D color_tx, vec2 input_texel) { @@ -390,7 +365,7 @@ vec4 film_amend_combined_history( float t = line_aabb_clipping_dist(color_history.rgb, clip_dir.rgb, min_color.rgb, max_color.rgb); color_history.rgb += clip_dir.rgb * saturate(t); - /* Clip alpha on its own to avoid interference with other chanels. */ + /* Clip alpha on its own to avoid interference with other channels. */ float t_a = film_aabb_clipping_dist_alpha(color_history.a, clip_dir.a, min_color.a, max_color.a); color_history.a += clip_dir.a * saturate(t_a); @@ -406,16 +381,16 @@ float film_history_blend_factor(float velocity, { /* 5% of incoming color by default. */ float blend = 0.05; - /* Blend less history if the pixel has substential velocity. */ + /* Blend less history if the pixel has substantial velocity. */ blend = mix(blend, 0.20, saturate(velocity * 0.02)); /** * "High Quality Temporal Supersampling" by Brian Karis at Siggraph 2014 (Slide 43) - * Bias towards history if incomming pixel is near clamping. Reduces flicker. + * Bias towards history if incoming pixel is near clamping. Reduces flicker. */ float distance_to_luma_clip = min_v2(vec2(luma_history - luma_min, luma_max - luma_history)); /* Divide by bbox size to get a factor. 2 factor to compensate the line above. */ distance_to_luma_clip *= 2.0 * safe_rcp(luma_max - luma_min); - /* Linearly blend when history gets bellow to 25% of the bbox size. */ + /* Linearly blend when history gets below to 25% of the bbox size. */ blend *= saturate(distance_to_luma_clip * 4.0 + 0.1); /* Discard out of view history. */ if (any(lessThan(texel, vec2(0))) || any(greaterThanEqual(texel, film_buf.extent))) { @@ -451,13 +426,13 @@ void film_store_combined( float velocity = length(motion); - /* Load weight if it is not uniform accross the whole buffer (i.e: upsampling, panoramic). */ + /* Load weight if it is not uniform across the whole buffer (i.e: upsampling, panoramic). */ // dst.weight = film_weight_load(texel_combined); color_dst = film_sample_catmull_rom(in_combined_tx, history_texel); - color_dst.rgb = film_YCoCg_from_scene_linear(color_dst.rgb); + color_dst.rgb = colorspace_YCoCg_from_scene_linear(color_dst.rgb); - /* Get local color bounding box of source neighboorhood. */ + /* Get local color bounding box of source neighborhood. */ vec4 min_color, max_color; film_combined_neighbor_boundbox(src_texel, min_color, max_color); @@ -473,7 +448,7 @@ void film_store_combined( else { /* Everything is static. Use render accumulation. */ color_dst = texelFetch(in_combined_tx, dst.texel, 0); - color_dst.rgb = film_YCoCg_from_scene_linear(color_dst.rgb); + color_dst.rgb = colorspace_YCoCg_from_scene_linear(color_dst.rgb); /* Luma weighted blend to avoid flickering. */ weight_dst = film_luma_weight(color_dst.x) * dst.weight; @@ -483,7 +458,7 @@ void film_store_combined( color = color_dst * weight_dst + color_src * weight_src; color /= weight_src + weight_dst; - color.rgb = film_scene_linear_from_YCoCg(color.rgb); + color.rgb = colorspace_scene_linear_from_YCoCg(color.rgb); /* Fix alpha not accumulating to 1 because of float imprecision. */ if (color.a > 0.995) { @@ -622,7 +597,7 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth src = film_sample_get(i, texel_film); film_sample_accum_combined(src, combined_accum, weight_accum); } - /* NOTE: src.texel is center texel in incomming data buffer. */ + /* NOTE: src.texel is center texel in incoming data buffer. */ film_store_combined(dst, src.texel, combined_accum, weight_accum, out_color); } @@ -636,6 +611,8 @@ void film_process_data(ivec2 texel_film, out vec4 out_color, out float out_depth vec4 normal = texelFetch(normal_tx, film_sample.texel, 0); float depth = texelFetch(depth_tx, film_sample.texel, 0).x; vec4 vector = velocity_resolve(vector_tx, film_sample.texel, depth); + /* Transform to pixel space. */ + vector *= vec4(film_buf.render_extent, -film_buf.render_extent); film_store_depth(texel_film, depth, out_depth); film_store_data(texel_film, film_buf.normal_id, normal, out_color); diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl new file mode 100644 index 00000000000..321c99f7952 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_debug_frag.glsl @@ -0,0 +1,52 @@ + +/** + * Debug Shader outputing a gradient of orange - white - blue to mark culling hotspots. + * Green pixels are error pixels that are missing lights from the culling pass (i.e: when culling + * pass is not conservative enough). + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_FragCoord.xy); + + float depth = texelFetch(depth_tx, texel, 0).r; + float vP_z = get_view_z_from_depth(depth); + vec3 P = get_world_space_from_depth(uvcoordsvar.xy, depth); + + float light_count = 0.0; + uint light_cull = 0u; + vec2 px = gl_FragCoord.xy; + LIGHT_FOREACH_BEGIN_LOCAL(light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx) + { + LightData light = light_buf[l_idx]; + light_cull |= 1u << l_idx; + light_count += 1.0; + } + LIGHT_FOREACH_END + + uint light_nocull = 0u; + LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(light_cull_buf, l_idx) + { + LightData light = light_buf[l_idx]; + vec3 L; + float dist; + light_vector_get(light, P, L, dist); + if (light_attenuation(light_buf[l_idx], L, dist) > 0.0) { + light_nocull |= 1u << l_idx; + } + } + LIGHT_FOREACH_END + + if ((light_cull & light_nocull) != light_nocull) { + /* ERROR. Some lights were culled incorrectly. */ + out_debug_color = vec4(0.0, 1.0, 0.0, 1.0); + } + else { + out_debug_color = vec4(heatmap_gradient(light_count / 4.0), 1.0); + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl new file mode 100644 index 00000000000..9c12b0e50e6 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_select_comp.glsl @@ -0,0 +1,62 @@ + +/** + * Select the visible items inside the active view and put them inside the sorting buffer. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) + +void main() +{ + uint l_idx = gl_GlobalInvocationID.x; + if (l_idx >= light_cull_buf.items_count) { + return; + } + + LightData light = in_light_buf[l_idx]; + + /* Do not select 0 power lights. */ + if (light.influence_radius_max < 1e-8) { + return; + } + + /* Sun lights are packed at the end of the array. Perform early copy. */ + if (light.type == LIGHT_SUN) { + /* NOTE: We know the index because sun lights are packed at the start of the input buffer. */ + out_light_buf[light_cull_buf.local_lights_len + l_idx] = light; + return; + } + + Sphere sphere; + switch (light.type) { + case LIGHT_SPOT: + /* Only for < ~170° Cone due to plane extraction precision. */ + if (light.spot_tan < 10.0) { + Pyramid pyramid = shape_pyramid_non_oblique( + light._position, + light._position - light._back * light.influence_radius_max, + light._right * light.influence_radius_max * light.spot_tan / light.spot_size_inv.x, + light._up * light.influence_radius_max * light.spot_tan / light.spot_size_inv.y); + if (!intersect_view(pyramid)) { + return; + } + } + case LIGHT_RECT: + case LIGHT_ELLIPSE: + case LIGHT_POINT: + sphere = Sphere(light._position, light.influence_radius_max); + break; + } + + /* TODO(fclem): HiZ culling? Could be quite beneficial given the nature of the 2.5D culling. */ + + /* TODO(fclem): Small light culling / fading? */ + + if (intersect_view(sphere)) { + uint index = atomicAdd(light_cull_buf.visible_count, 1u); + + out_zdist_buf[index] = dot(cameraForward, light._position) - dot(cameraForward, cameraPos); + out_key_buf[index] = l_idx; + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl new file mode 100644 index 00000000000..daf2016cd35 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_sort_comp.glsl @@ -0,0 +1,57 @@ + +/** + * Sort the lights by their Z distance to the camera. + * Outputs ordered light buffer. + * One thread processes one Light entity. + */ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +shared float zdists_cache[gl_WorkGroupSize.x]; + +void main() +{ + uint src_index = gl_GlobalInvocationID.x; + bool valid_thread = true; + + if (src_index >= light_cull_buf.visible_count) { + /* Do not return because we use barriers later on (which need uniform control flow). + * Just process the same last item but avoid insertion. */ + src_index = light_cull_buf.visible_count - 1; + valid_thread = false; + } + + float local_zdist = in_zdist_buf[src_index]; + + int prefix_sum = 0; + /* Iterate over the whole key buffer. */ + uint iter = divide_ceil_u(light_cull_buf.visible_count, gl_WorkGroupSize.x); + for (uint i = 0u; i < iter; i++) { + uint index = gl_WorkGroupSize.x * i + gl_LocalInvocationID.x; + /* NOTE: This will load duplicated values, but they will be discarded. */ + index = min(index, light_cull_buf.visible_count - 1); + zdists_cache[gl_LocalInvocationID.x] = in_zdist_buf[index]; + + barrier(); + + /* Iterate over the cache line. */ + uint line_end = min(gl_WorkGroupSize.x, light_cull_buf.visible_count - gl_WorkGroupSize.x * i); + for (uint j = 0u; j < line_end; j++) { + if (zdists_cache[j] < local_zdist) { + prefix_sum++; + } + else if (zdists_cache[j] == local_zdist) { + /* Same depth, use index to order and avoid same prefix for 2 different lights. */ + if ((gl_WorkGroupSize.x * i + j) < src_index) { + prefix_sum++; + } + } + } + } + + if (valid_thread) { + /* Copy sorted light to render light buffer. */ + uint input_index = in_key_buf[src_index]; + out_light_buf[prefix_sum] = in_light_buf[input_index]; + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl new file mode 100644 index 00000000000..37705e22b22 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_tile_comp.glsl @@ -0,0 +1,188 @@ + +/** + * 2D Culling pass for lights. + * We iterate over all items and check if they intersect with the tile frustum. + * Dispatch one thread per word. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_intersect_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Culling shapes extraction + * \{ */ + +struct CullingTile { + IsectFrustum frustum; + vec4 bounds; +}; + +/* Corners are expected to be in viewspace so that the cone is starting from the origin. + * Corner order does not matter. */ +vec4 tile_bound_cone(vec3 v00, vec3 v01, vec3 v10, vec3 v11) +{ + v00 = normalize(v00); + v01 = normalize(v01); + v10 = normalize(v10); + v11 = normalize(v11); + vec3 center = normalize(v00 + v01 + v10 + v11); + float angle_cosine = dot(center, v00); + angle_cosine = max(angle_cosine, dot(center, v01)); + angle_cosine = max(angle_cosine, dot(center, v10)); + angle_cosine = max(angle_cosine, dot(center, v11)); + return vec4(center, angle_cosine); +} + +/* Corners are expected to be in viewspace. Returns Z-aligned bounding cylinder. + * Corner order does not matter. */ +vec4 tile_bound_cylinder(vec3 v00, vec3 v01, vec3 v10, vec3 v11) +{ + vec3 center = (v00 + v01 + v10 + v11) * 0.25; + vec4 corners_dist; + float dist_sqr = distance_squared(center, v00); + dist_sqr = max(dist_sqr, distance_squared(center, v01)); + dist_sqr = max(dist_sqr, distance_squared(center, v10)); + dist_sqr = max(dist_sqr, distance_squared(center, v11)); + /* Return a cone. Later converted to cylinder. */ + return vec4(center, sqrt(dist_sqr)); +} + +vec2 tile_to_ndc(vec2 tile_co, vec2 offset) +{ + /* Add a margin to prevent culling too much if the frustum becomes too much unstable. */ + const float margin = 0.02; + tile_co += margin * (offset * 2.0 - 1.0); + + tile_co += offset; + return tile_co * light_cull_buf.tile_to_uv_fac * 2.0 - 1.0; +} + +CullingTile tile_culling_get(uvec2 tile_co) +{ + vec2 ftile = vec2(tile_co); + /* Culling frustum corners for this tile. */ + vec3 corners[8]; + /* Follow same corners order as view frustum. */ + corners[1].xy = corners[0].xy = tile_to_ndc(ftile, vec2(0, 0)); + corners[5].xy = corners[4].xy = tile_to_ndc(ftile, vec2(1, 0)); + corners[6].xy = corners[7].xy = tile_to_ndc(ftile, vec2(1, 1)); + corners[2].xy = corners[3].xy = tile_to_ndc(ftile, vec2(0, 1)); + corners[1].z = corners[5].z = corners[6].z = corners[2].z = -1.0; + corners[0].z = corners[4].z = corners[7].z = corners[3].z = 1.0; + + for (int i = 0; i < 8; i++) { + /* Culling in view space for precision. */ + corners[i] = project_point(ProjectionMatrixInverse, corners[i]); + } + + bool is_persp = ProjectionMatrix[3][3] == 0.0; + CullingTile tile; + tile.bounds = (is_persp) ? tile_bound_cone(corners[0], corners[4], corners[7], corners[3]) : + tile_bound_cylinder(corners[0], corners[4], corners[7], corners[3]); + + tile.frustum = isect_data_setup(shape_frustum(corners)); + return tile; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Intersection Tests + * \{ */ + +bool intersect(CullingTile tile, Sphere sphere) +{ + bool isect = true; + /* Test tile intersection using bounding cone or bounding cylinder. + * This has less false positive cases when the sphere is large. */ + if (ProjectionMatrix[3][3] == 0.0) { + isect = intersect(shape_cone(tile.bounds.xyz, tile.bounds.w), sphere); + } + else { + /* Simplify to a 2D circle test on the view Z axis plane. */ + isect = intersect(shape_circle(tile.bounds.xy, tile.bounds.w), + shape_circle(sphere.center.xy, sphere.radius)); + } + /* Refine using frustum test. If the sphere is small it avoids intersection + * with a neighbor tile. */ + if (isect) { + isect = intersect(tile.frustum, sphere); + } + return isect; +} + +bool intersect(CullingTile tile, Box bbox) +{ + return intersect(tile.frustum, bbox); +} + +bool intersect(CullingTile tile, Pyramid pyramid) +{ + return intersect(tile.frustum, pyramid); +} + +/** \} */ + +void main() +{ + uint word_idx = gl_GlobalInvocationID.x % light_cull_buf.tile_word_len; + uint tile_idx = gl_GlobalInvocationID.x / light_cull_buf.tile_word_len; + uvec2 tile_co = uvec2(tile_idx % light_cull_buf.tile_x_len, + tile_idx / light_cull_buf.tile_x_len); + + if (tile_co.y >= light_cull_buf.tile_y_len) { + return; + } + + /* TODO(fclem): We could stop the tile at the HiZ depth. */ + CullingTile tile = tile_culling_get(tile_co); + + uint l_idx = word_idx * 32u; + uint l_end = min(l_idx + 32u, light_cull_buf.visible_count); + uint word = 0u; + for (; l_idx < l_end; l_idx++) { + LightData light = light_buf[l_idx]; + + /* Culling in view space for precision and simplicity. */ + vec3 vP = transform_point(ViewMatrix, light._position); + vec3 v_right = transform_direction(ViewMatrix, light._right); + vec3 v_up = transform_direction(ViewMatrix, light._up); + vec3 v_back = transform_direction(ViewMatrix, light._back); + float radius = light.influence_radius_max; + + Sphere sphere = shape_sphere(vP, radius); + bool intersect_tile = intersect(tile, sphere); + + switch (light.type) { + case LIGHT_SPOT: + /* Only for < ~170° Cone due to plane extraction precision. */ + if (light.spot_tan < 10.0) { + Pyramid pyramid = shape_pyramid_non_oblique( + vP, + vP - v_back * radius, + v_right * radius * light.spot_tan / light.spot_size_inv.x, + v_up * radius * light.spot_tan / light.spot_size_inv.y); + intersect_tile = intersect_tile && intersect(tile, pyramid); + break; + } + /* Fallthrough to the hemispheric case. */ + case LIGHT_RECT: + case LIGHT_ELLIPSE: + vec3 v000 = vP - v_right * radius - v_up * radius; + vec3 v100 = v000 + v_right * (radius * 2.0); + vec3 v010 = v000 + v_up * (radius * 2.0); + vec3 v001 = v000 - v_back * radius; + Box bbox = shape_box(v000, v100, v010, v001); + intersect_tile = intersect_tile && intersect(tile, bbox); + default: + break; + } + + if (intersect_tile) { + word |= 1u << (l_idx % 32u); + } + } + + out_light_tile_buf[gl_GlobalInvocationID.x] = word; +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl new file mode 100644 index 00000000000..d96f191fb77 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_culling_zbin_comp.glsl @@ -0,0 +1,56 @@ + +/** + * Create the Zbins from Z-sorted lights. + * Perform min-max operation in LDS memory for speed. + * For this reason, we only dispatch 1 thread group. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl) + +/* Fits the limit of 32KB. */ +shared uint zbin_max[CULLING_ZBIN_COUNT]; +shared uint zbin_min[CULLING_ZBIN_COUNT]; + +void main() +{ + const uint zbin_iter = CULLING_ZBIN_COUNT / gl_WorkGroupSize.x; + const uint zbin_local = gl_LocalInvocationID.x * zbin_iter; + + uint src_index = gl_GlobalInvocationID.x; + + for (uint i = 0u, l = zbin_local; i < zbin_iter; i++, l++) { + zbin_max[l] = 0x0u; + zbin_min[l] = ~0x0u; + } + barrier(); + + uint light_iter = divide_ceil_u(light_cull_buf.visible_count, gl_WorkGroupSize.x); + for (uint i = 0u; i < light_iter; i++) { + uint index = i * gl_WorkGroupSize.x + gl_LocalInvocationID.x; + if (index >= light_cull_buf.visible_count) { + continue; + } + vec3 P = light_buf[index]._position; + /* TODO(fclem): Could have better bounds for spot and area lights. */ + float radius = light_buf[index].influence_radius_max; + float z_dist = dot(cameraForward, P) - dot(cameraForward, cameraPos); + int z_min = culling_z_to_zbin( + light_cull_buf.zbin_scale, light_cull_buf.zbin_bias, z_dist + radius); + int z_max = culling_z_to_zbin( + light_cull_buf.zbin_scale, light_cull_buf.zbin_bias, z_dist - radius); + z_min = clamp(z_min, 0, CULLING_ZBIN_COUNT - 1); + z_max = clamp(z_max, 0, CULLING_ZBIN_COUNT - 1); + /* Register to Z bins. */ + for (int z = z_min; z <= z_max; z++) { + atomicMin(zbin_min[z], index); + atomicMax(zbin_max[z], index); + } + } + barrier(); + + /* Write result to zbins buffer. Pack min & max into 1 uint. */ + for (uint i = 0u, l = zbin_local; i < zbin_iter; i++, l++) { + out_zbin_buf[l] = (zbin_max[l] << 16u) | (zbin_min[l] & 0xFFFFu); + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl new file mode 100644 index 00000000000..d4abdd43aa4 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_eval_lib.glsl @@ -0,0 +1,129 @@ + +/** + * The resources expected to be defined are: + * - light_buf + * - light_zbin_buf + * - light_cull_buf + * - light_tile_buf + * - shadow_atlas_tx + * - shadow_tilemaps_tx + * - sss_transmittance_tx + * - utility_tx + */ + +#pragma BLENDER_REQUIRE(eevee_light_lib.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_codegen_lib.glsl) + +/* TODO(fclem): We could reduce register pressure by only having static branches for sun lights. */ +void light_eval_ex(ClosureDiffuse diffuse, + ClosureReflection reflection, + const bool is_directional, + vec3 P, + vec3 V, + float vP_z, + float thickness, + vec4 ltc_mat, + uint l_idx, + inout vec3 out_diffuse, + inout vec3 out_specular) +{ + LightData light = light_buf[l_idx]; + vec3 L; + float dist; + light_vector_get(light, P, L, dist); + + float visibility = light_attenuation(light, L, dist); + +#if 0 /* TODO(fclem): Shadows */ + if ((light.shadow_id != LIGHT_NO_SHADOW) && (visibility > 0.0)) { + vec3 lL = light_world_to_local(light, -L) * dist; + + float shadow_delta = shadow_delta_get( + shadow_atlas_tx, shadow_tilemaps_tx, light, light.shadow_data, lL, dist, P); + +# ifdef SSS_TRANSMITTANCE + /* Transmittance evaluation first to use initial visibility. */ + if (diffuse.sss_id != 0u && light.diffuse_power > 0.0) { + float delta = max(thickness, shadow_delta); + + vec3 intensity = visibility * light.transmit_power * + light_translucent(sss_transmittance_tx, + is_directional, + light, + diffuse.N, + L, + dist, + diffuse.sss_radius, + delta); + out_diffuse += light.color * intensity; + } +# endif + + visibility *= float(shadow_delta - light.shadow_data.bias <= 0.0); + } +#endif + + if (visibility < 1e-6) { + return; + } + + if (light.diffuse_power > 0.0) { + float intensity = visibility * light.diffuse_power * + light_diffuse(utility_tx, is_directional, light, diffuse.N, V, L, dist); + out_diffuse += light.color * intensity; + } + + if (light.specular_power > 0.0) { + float intensity = visibility * light.specular_power * + light_ltc( + utility_tx, is_directional, light, reflection.N, V, L, dist, ltc_mat); + out_specular += light.color * intensity; + } +} + +void light_eval(ClosureDiffuse diffuse, + ClosureReflection reflection, + vec3 P, + vec3 V, + float vP_z, + float thickness, + inout vec3 out_diffuse, + inout vec3 out_specular) +{ + vec2 uv = vec2(reflection.roughness, safe_sqrt(1.0 - dot(reflection.N, V))); + uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS; + vec4 ltc_mat = utility_tx_sample(utility_tx, uv, UTIL_LTC_MAT_LAYER); + + LIGHT_FOREACH_BEGIN_DIRECTIONAL(light_cull_buf, l_idx) + { + light_eval_ex(diffuse, + reflection, + true, + P, + V, + vP_z, + thickness, + ltc_mat, + l_idx, + out_diffuse, + out_specular); + } + LIGHT_FOREACH_END + + vec2 px = gl_FragCoord.xy; + LIGHT_FOREACH_BEGIN_LOCAL(light_cull_buf, light_zbin_buf, light_tile_buf, px, vP_z, l_idx) + { + light_eval_ex(diffuse, + reflection, + false, + P, + V, + vP_z, + thickness, + ltc_mat, + l_idx, + out_diffuse, + out_specular); + } + LIGHT_FOREACH_END +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_iter_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_iter_lib.glsl new file mode 100644 index 00000000000..22a5f98e6c3 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_iter_lib.glsl @@ -0,0 +1,72 @@ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +uint zbin_mask(uint word_index, uint zbin_min, uint zbin_max) +{ + uint word_start = word_index * 32u; + uint word_end = word_start + 31u; + uint local_min = max(zbin_min, word_start); + uint local_max = min(zbin_max, word_end); + uint mask_width = local_max - local_min + 1; + return bit_field_mask(mask_width, local_min); +} + +int culling_z_to_zbin(float scale, float bias, float z) +{ + return int(z * scale + bias); +} + +/* Waiting to implement extensions support. We need: + * - GL_KHR_shader_subgroup_ballot + * - GL_KHR_shader_subgroup_arithmetic + * or + * - Vulkan 1.1 + */ +#if 1 +# define subgroupMin(a) a +# define subgroupMax(a) a +# define subgroupOr(a) a +# define subgroupBroadcastFirst(a) a +#endif + +#define LIGHT_FOREACH_BEGIN_DIRECTIONAL(_culling, _index) \ + { \ + { \ + for (uint _index = _culling.local_lights_len; _index < _culling.items_count; _index++) { + +#define LIGHT_FOREACH_BEGIN_LOCAL(_culling, _zbins, _words, _pixel, _linearz, _item_index) \ + { \ + uvec2 tile_co = uvec2(_pixel / _culling.tile_size); \ + uint tile_word_offset = (tile_co.x + tile_co.y * _culling.tile_x_len) * \ + _culling.tile_word_len; \ + int zbin_index = culling_z_to_zbin(_culling.zbin_scale, _culling.zbin_bias, _linearz); \ + zbin_index = clamp(zbin_index, 0, CULLING_ZBIN_COUNT - 1); \ + uint zbin_data = _zbins[zbin_index]; \ + uint min_index = zbin_data & 0xFFFFu; \ + uint max_index = zbin_data >> 16u; \ + /* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \ + min_index = subgroupBroadcastFirst(subgroupMin(min_index)); \ + max_index = subgroupBroadcastFirst(subgroupMax(max_index)); \ + /* Same as divide by 32 but avoid interger division. */ \ + uint word_min = min_index >> 5u; \ + uint word_max = max_index >> 5u; \ + for (uint word_idx = word_min; word_idx <= word_max; word_idx++) { \ + uint word = _words[tile_word_offset + word_idx]; \ + word &= zbin_mask(word_idx, min_index, max_index); \ + /* Ensure all threads inside a subgroup get the same value to reduce VGPR usage. */ \ + word = subgroupBroadcastFirst(subgroupOr(word)); \ + int bit_index; \ + while ((bit_index = findLSB(word)) != -1) { \ + word &= ~1u << uint(bit_index); \ + uint _item_index = word_idx * 32u + bit_index; + +/* No culling. Iterate over all items. */ +#define LIGHT_FOREACH_BEGIN_LOCAL_NO_CULL(_culling, _item_index) \ + { \ + { \ + for (uint _item_index = 0; _item_index < _culling.visible_count; _item_index++) { + +#define LIGHT_FOREACH_END \ + } \ + } \ + } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl new file mode 100644 index 00000000000..58608f6e1f0 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_light_lib.glsl @@ -0,0 +1,209 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_ltc_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_iter_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Light Functions + * \{ */ + +void light_vector_get(LightData ld, vec3 P, out vec3 L, out float dist) +{ + if (ld.type == LIGHT_SUN) { + L = ld._back; + dist = 1.0; + } + else { + L = ld._position - P; + dist = inversesqrt(len_squared(L)); + L *= dist; + dist = 1.0 / dist; + } +} + +/* Rotate vector to light's local space. Does not translate. */ +vec3 light_world_to_local(LightData ld, vec3 L) +{ + /* Avoid relying on compiler to optimize this. + * vec3 lL = transpose(mat3(ld.object_mat)) * L; */ + vec3 lL; + lL.x = dot(ld.object_mat[0].xyz, L); + lL.y = dot(ld.object_mat[1].xyz, L); + lL.z = dot(ld.object_mat[2].xyz, L); + return lL; +} + +/* From Frostbite PBR Course + * Distance based attenuation + * http://www.frostbite.com/wp-content/uploads/2014/11/course_notes_moving_frostbite_to_pbr.pdf */ +float light_influence_attenuation(float dist, float inv_sqr_influence) +{ + float factor = sqr(dist) * inv_sqr_influence; + float fac = saturate(1.0 - sqr(factor)); + return sqr(fac); +} + +float light_spot_attenuation(LightData ld, vec3 L) +{ + vec3 lL = light_world_to_local(ld, L); + float ellipse = inversesqrt(1.0 + len_squared(lL.xy * ld.spot_size_inv / lL.z)); + float spotmask = smoothstep(0.0, 1.0, ellipse * ld._spot_mul + ld._spot_bias); + return spotmask; +} + +float light_attenuation(LightData ld, vec3 L, float dist) +{ + float vis = 1.0; + if (ld.type == LIGHT_SPOT) { + vis *= light_spot_attenuation(ld, L); + } + if (ld.type >= LIGHT_SPOT) { + vis *= step(0.0, -dot(L, -ld._back)); + } + if (ld.type != LIGHT_SUN) { +#ifdef VOLUME_LIGHTING + vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_volume); +#else + vis *= light_influence_attenuation(dist, ld.influence_radius_invsqr_surface); +#endif + } + return vis; +} + +/* Cheaper alternative than evaluating the LTC. + * The result needs to be multiplied by BSDF or Phase Function. */ +float light_point_light(LightData ld, const bool is_directional, vec3 L, float dist) +{ + if (is_directional) { + return 1.0; + } + /** + * Using "Point Light Attenuation Without Singularity" from Cem Yuksel + * http://www.cemyuksel.com/research/pointlightattenuation/pointlightattenuation.pdf + * http://www.cemyuksel.com/research/pointlightattenuation/ + **/ + float d_sqr = sqr(dist); + float r_sqr = ld.radius_squared; + /* Using reformulation that has better numerical percision. */ + float power = 2.0 / (d_sqr + r_sqr + dist * sqrt(d_sqr + r_sqr)); + + if (is_area_light(ld.type)) { + /* Modulate by light plane orientation / solid angle. */ + power *= saturate(dot(ld._back, L)); + } + return power; +} + +float light_diffuse(sampler2DArray utility_tx, + const bool is_directional, + LightData ld, + vec3 N, + vec3 V, + vec3 L, + float dist) +{ + if (is_directional || !is_area_light(ld.type)) { + float radius = ld._radius / dist; + return ltc_evaluate_disk_simple(utility_tx, radius, dot(N, L)); + } + else if (ld.type == LIGHT_RECT) { + vec3 corners[4]; + corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y; + corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y; + corners[2] = -corners[0]; + corners[3] = -corners[1]; + + corners[0] = normalize(L * dist + corners[0]); + corners[1] = normalize(L * dist + corners[1]); + corners[2] = normalize(L * dist + corners[2]); + corners[3] = normalize(L * dist + corners[3]); + + return ltc_evaluate_quad(utility_tx, corners, N); + } + else /* (ld.type == LIGHT_ELLIPSE) */ { + vec3 points[3]; + points[0] = ld._right * -ld._area_size_x + ld._up * -ld._area_size_y; + points[1] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y; + points[2] = -points[0]; + + points[0] += L * dist; + points[1] += L * dist; + points[2] += L * dist; + + return ltc_evaluate_disk(utility_tx, N, V, mat3(1.0), points); + } +} + +float light_ltc(sampler2DArray utility_tx, + const bool is_directional, + LightData ld, + vec3 N, + vec3 V, + vec3 L, + float dist, + vec4 ltc_mat) +{ + if (is_directional || ld.type != LIGHT_RECT) { + vec3 Px = ld._right; + vec3 Py = ld._up; + + if (is_directional || !is_area_light(ld.type)) { + make_orthonormal_basis(L, Px, Py); + } + + vec3 points[3]; + points[0] = Px * -ld._area_size_x + Py * -ld._area_size_y; + points[1] = Px * ld._area_size_x + Py * -ld._area_size_y; + points[2] = -points[0]; + + points[0] += L * dist; + points[1] += L * dist; + points[2] += L * dist; + + return ltc_evaluate_disk(utility_tx, N, V, ltc_matrix(ltc_mat), points); + } + else { + vec3 corners[4]; + corners[0] = ld._right * ld._area_size_x + ld._up * -ld._area_size_y; + corners[1] = ld._right * ld._area_size_x + ld._up * ld._area_size_y; + corners[2] = -corners[0]; + corners[3] = -corners[1]; + + corners[0] += L * dist; + corners[1] += L * dist; + corners[2] += L * dist; + corners[3] += L * dist; + + ltc_transform_quad(N, V, ltc_matrix(ltc_mat), corners); + + return ltc_evaluate_quad(utility_tx, corners, vec3(0.0, 0.0, 1.0)); + } +} + +vec3 light_translucent(sampler1D transmittance_tx, + const bool is_directional, + LightData ld, + vec3 N, + vec3 L, + float dist, + vec3 sss_radius, + float delta) +{ + /* TODO(fclem): We should compute the power at the entry point. */ + /* NOTE(fclem): we compute the light attenuation using the light vector but the transmittance + * using the shadow depth delta. */ + float power = light_point_light(ld, is_directional, L, dist); + /* Do not add more energy on front faces. Also apply lambertian BSDF. */ + power *= max(0.0, dot(-N, L)) * M_1_PI; + + sss_radius *= SSS_TRANSMIT_LUT_RADIUS; + vec3 channels_co = saturate(delta / sss_radius) * SSS_TRANSMIT_LUT_SCALE + SSS_TRANSMIT_LUT_BIAS; + + vec3 translucency; + translucency.x = (sss_radius.x > 0.0) ? texture(transmittance_tx, channels_co.x).r : 0.0; + translucency.y = (sss_radius.y > 0.0) ? texture(transmittance_tx, channels_co.y).r : 0.0; + translucency.z = (sss_radius.z > 0.0) ? texture(transmittance_tx, channels_co.z).r : 0.0; + return translucency * power; +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_ltc_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_ltc_lib.glsl new file mode 100644 index 00000000000..57e92b0b9b4 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_ltc_lib.glsl @@ -0,0 +1,299 @@ + +/** + * Adapted from : + * Real-Time Polygonal-Light Shading with Linearly Transformed Cosines. + * Eric Heitz, Jonathan Dupuy, Stephen Hill and David Neubelt. + * ACM Transactions on Graphics (Proceedings of ACM SIGGRAPH 2016) 35(4), 2016. + * Project page: https://eheitzresearch.wordpress.com/415-2/ + */ + +/* Diffuse *clipped* sphere integral. */ +float ltc_diffuse_sphere_integral(sampler2DArray utility_tx, float avg_dir_z, float form_factor) +{ +#if 1 + /* use tabulated horizon-clipped sphere */ + vec2 uv = vec2(avg_dir_z * 0.5 + 0.5, form_factor); + uv = uv * UTIL_TEX_UV_SCALE + UTIL_TEX_UV_BIAS; + + return texture(utility_tx, vec3(uv, UTIL_DISK_INTEGRAL_LAYER))[UTIL_DISK_INTEGRAL_COMP]; +#else + /* Cheap approximation. Less smooth and have energy issues. */ + return max((form_factor * form_factor + avg_dir_z) / (form_factor + 1.0), 0.0); +#endif +} + +/** + * An extended version of the implementation from + * "How to solve a cubic equation, revisited" + * http://momentsingraphics.de/?p=105 + */ +vec3 ltc_solve_cubic(vec4 coefs) +{ + /* Normalize the polynomial */ + coefs.xyz /= coefs.w; + /* Divide middle coefficients by three */ + coefs.yz /= 3.0; + + float A = coefs.w; + float B = coefs.z; + float C = coefs.y; + float D = coefs.x; + + /* Compute the Hessian and the discriminant */ + vec3 delta = vec3(-coefs.zy * coefs.zz + coefs.yx, dot(vec2(coefs.z, -coefs.y), coefs.xy)); + + /* Discriminant */ + float discr = dot(vec2(4.0 * delta.x, -delta.y), delta.zy); + + /* Clamping avoid NaN output on some platform. (see T67060) */ + float sqrt_discr = sqrt(clamp(discr, 0.0, FLT_MAX)); + + vec2 xlc, xsc; + + /* Algorithm A */ + { + float A_a = 1.0; + float C_a = delta.x; + float D_a = -2.0 * B * delta.x + delta.y; + + /* Take the cubic root of a normalized complex number */ + float theta = atan(sqrt_discr, -D_a) / 3.0; + + float _2_sqrt_C_a = 2.0 * sqrt(-C_a); + float x_1a = _2_sqrt_C_a * cos(theta); + float x_3a = _2_sqrt_C_a * cos(theta + (2.0 / 3.0) * M_PI); + + float xl; + if ((x_1a + x_3a) > 2.0 * B) { + xl = x_1a; + } + else { + xl = x_3a; + } + + xlc = vec2(xl - B, A); + } + + /* Algorithm D */ + { + float A_d = D; + float C_d = delta.z; + float D_d = -D * delta.y + 2.0 * C * delta.z; + + /* Take the cubic root of a normalized complex number */ + float theta = atan(D * sqrt_discr, -D_d) / 3.0; + + float _2_sqrt_C_d = 2.0 * sqrt(-C_d); + float x_1d = _2_sqrt_C_d * cos(theta); + float x_3d = _2_sqrt_C_d * cos(theta + (2.0 / 3.0) * M_PI); + + float xs; + if (x_1d + x_3d < 2.0 * C) { + xs = x_1d; + } + else { + xs = x_3d; + } + + xsc = vec2(-D, xs + C); + } + + float E = xlc.y * xsc.y; + float F = -xlc.x * xsc.y - xlc.y * xsc.x; + float G = xlc.x * xsc.x; + + vec2 xmc = vec2(C * F - B * G, -B * F + C * E); + + vec3 root = vec3(xsc.x / xsc.y, xmc.x / xmc.y, xlc.x / xlc.y); + + if (root.x < root.y && root.x < root.z) { + root.xyz = root.yxz; + } + else if (root.z < root.x && root.z < root.y) { + root.xyz = root.xzy; + } + + return root; +} + +/* from Real-Time Area Lighting: a Journey from Research to Production + * Stephen Hill and Eric Heitz */ +vec3 ltc_edge_integral_vec(vec3 v1, vec3 v2) +{ + float x = dot(v1, v2); + float y = abs(x); + + float a = 0.8543985 + (0.4965155 + 0.0145206 * y) * y; + float b = 3.4175940 + (4.1616724 + y) * y; + float v = a / b; + + float theta_sintheta = (x > 0.0) ? v : 0.5 * inversesqrt(max(1.0 - x * x, 1e-7)) - v; + + return cross(v1, v2) * theta_sintheta; +} + +mat3 ltc_matrix(vec4 lut) +{ + /* Load inverse matrix. */ + return mat3(vec3(lut.x, 0, lut.y), vec3(0, 1, 0), vec3(lut.z, 0, lut.w)); +} + +void ltc_transform_quad(vec3 N, vec3 V, mat3 Minv, inout vec3 corners[4]) +{ + /* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */ + V = normalize(V + 1e-8); + + /* Construct orthonormal basis around N. */ + vec3 T1, T2; + T1 = normalize(V - N * dot(N, V)); + T2 = cross(N, T1); + + /* Rotate area light in (T1, T2, R) basis. */ + Minv = Minv * transpose(mat3(T1, T2, N)); + + /* Apply LTC inverse matrix. */ + corners[0] = normalize(Minv * corners[0]); + corners[1] = normalize(Minv * corners[1]); + corners[2] = normalize(Minv * corners[2]); + corners[3] = normalize(Minv * corners[3]); +} + +/* If corners have already pass through ltc_transform_quad(), + * then N **MUST** be vec3(0.0, 0.0, 1.0), corresponding to the Up axis of the shading basis. */ +float ltc_evaluate_quad(sampler2DArray utility_tx, vec3 corners[4], vec3 N) +{ + /* Approximation using a sphere of the same solid angle than the quad. + * Finding the clipped sphere diffuse integral is easier than clipping the quad. */ + vec3 avg_dir; + avg_dir = ltc_edge_integral_vec(corners[0], corners[1]); + avg_dir += ltc_edge_integral_vec(corners[1], corners[2]); + avg_dir += ltc_edge_integral_vec(corners[2], corners[3]); + avg_dir += ltc_edge_integral_vec(corners[3], corners[0]); + + float form_factor = length(avg_dir); + float avg_dir_z = dot(N, avg_dir / form_factor); + return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir_z, form_factor); +} + +/* If disk does not need to be transformed and is already front facing. */ +float ltc_evaluate_disk_simple(sampler2DArray utility_tx, float disk_radius, float NL) +{ + float r_sqr = disk_radius * disk_radius; + float one_r_sqr = 1.0 + r_sqr; + float form_factor = r_sqr * inversesqrt(one_r_sqr * one_r_sqr); + return form_factor * ltc_diffuse_sphere_integral(utility_tx, NL, form_factor); +} + +/* disk_points are WS vectors from the shading point to the disk "bounding domain" */ +float ltc_evaluate_disk(sampler2DArray utility_tx, vec3 N, vec3 V, mat3 Minv, vec3 disk_points[3]) +{ + /* Avoid dot(N, V) == 1 in ortho mode, leading T1 normalize to fail. */ + V = normalize(V + 1e-8); + + /* construct orthonormal basis around N */ + vec3 T1, T2; + T1 = normalize(V - N * dot(V, N)); + T2 = cross(N, T1); + + /* rotate area light in (T1, T2, R) basis */ + mat3 R = transpose(mat3(T1, T2, N)); + + /* Intermediate step: init ellipse. */ + vec3 L_[3]; + L_[0] = mul(R, disk_points[0]); + L_[1] = mul(R, disk_points[1]); + L_[2] = mul(R, disk_points[2]); + + vec3 C = 0.5 * (L_[0] + L_[2]); + vec3 V1 = 0.5 * (L_[1] - L_[2]); + vec3 V2 = 0.5 * (L_[1] - L_[0]); + + /* Transform ellipse by Minv. */ + C = Minv * C; + V1 = Minv * V1; + V2 = Minv * V2; + + /* Compute eigenvectors of new ellipse. */ + + float d11 = dot(V1, V1); + float d22 = dot(V2, V2); + float d12 = dot(V1, V2); + float a, b; /* Eigenvalues */ + const float threshold = 0.0007; /* Can be adjusted. Fix artifacts. */ + if (abs(d12) / sqrt(d11 * d22) > threshold) { + float tr = d11 + d22; + float det = -d12 * d12 + d11 * d22; + + /* use sqrt matrix to solve for eigenvalues */ + det = sqrt(det); + float u = 0.5 * sqrt(tr - 2.0 * det); + float v = 0.5 * sqrt(tr + 2.0 * det); + float e_max = (u + v); + float e_min = (u - v); + e_max *= e_max; + e_min *= e_min; + + vec3 V1_, V2_; + if (d11 > d22) { + V1_ = d12 * V1 + (e_max - d11) * V2; + V2_ = d12 * V1 + (e_min - d11) * V2; + } + else { + V1_ = d12 * V2 + (e_max - d22) * V1; + V2_ = d12 * V2 + (e_min - d22) * V1; + } + + a = 1.0 / e_max; + b = 1.0 / e_min; + V1 = normalize(V1_); + V2 = normalize(V2_); + } + else { + a = 1.0 / d11; + b = 1.0 / d22; + V1 *= sqrt(a); + V2 *= sqrt(b); + } + + /* Now find front facing ellipse with same solid angle. */ + + vec3 V3 = normalize(cross(V1, V2)); + if (dot(C, V3) < 0.0) { + V3 *= -1.0; + } + + float L = dot(V3, C); + float inv_L = 1.0 / L; + float x0 = dot(V1, C) * inv_L; + float y0 = dot(V2, C) * inv_L; + + float L_sqr = L * L; + a *= L_sqr; + b *= L_sqr; + + float t = 1.0 + x0 * x0; + float c0 = a * b; + float c1 = c0 * (t + y0 * y0) - a - b; + float c2 = (1.0 - a * t) - b * (1.0 + y0 * y0); + float c3 = 1.0; + + vec3 roots = ltc_solve_cubic(vec4(c0, c1, c2, c3)); + float e1 = roots.x; + float e2 = roots.y; + float e3 = roots.z; + + vec3 avg_dir = vec3(a * x0 / (a - e2), b * y0 / (b - e2), 1.0); + + mat3 rotate = mat3(V1, V2, V3); + + avg_dir = rotate * avg_dir; + avg_dir = normalize(avg_dir); + + /* L1, L2 are the extends of the front facing ellipse. */ + float L1 = sqrt(-e2 / e3); + float L2 = sqrt(-e2 / e1); + + /* Find the sphere and compute lighting. */ + float form_factor = max(0.0, L1 * L2 * inversesqrt((1.0 + L1 * L1) * (1.0 + L2 * L2))); + return form_factor * ltc_diffuse_sphere_integral(utility_tx, avg_dir.z, form_factor); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl new file mode 100644 index 00000000000..99186ab6f67 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_dilate_comp.glsl @@ -0,0 +1,116 @@ + +/** + * Dilate motion vector tiles until we covered maximum velocity. + * Outputs the largest intersecting motion vector in the neighborhood. + * + */ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl) + +#define DEBUG_BYPASS_DILATION 0 + +struct MotionRect { + ivec2 bottom_left; + ivec2 extent; +}; + +MotionRect compute_motion_rect(ivec2 tile, vec2 motion) +{ +#if DEBUG_BYPASS_DILATION + return MotionRect(tile, ivec2(1)); +#endif + /* Ceil to number of tile touched.*/ + ivec2 point1 = tile + ivec2(sign(motion) * ceil(abs(motion) / float(MOTION_BLUR_TILE_SIZE))); + ivec2 point2 = tile; + + ivec2 max_point = max(point1, point2); + ivec2 min_point = min(point1, point2); + /* Clamp to bounds. */ + max_point = min(max_point, imageSize(in_tiles_img) - 1); + min_point = max(min_point, ivec2(0)); + + MotionRect rect; + rect.bottom_left = min_point; + rect.extent = 1 + max_point - min_point; + return rect; +} + +struct MotionLine { + /** Origin of the line. */ + vec2 origin; + /** Normal to the line direction. */ + vec2 normal; +}; + +MotionLine compute_motion_line(ivec2 tile, vec2 motion) +{ + vec2 dir = safe_normalize(motion); + + MotionLine line; + line.origin = vec2(tile); + /* Rotate 90° Counter-Clockwise. */ + line.normal = vec2(-dir.y, dir.x); + return line; +} + +bool is_inside_motion_line(ivec2 tile, MotionLine motion_line) +{ +#if DEBUG_BYPASS_DILATION + return true; +#endif + /* NOTE: Everything in is tile unit. */ + float dist = point_line_projection_dist(vec2(tile), motion_line.origin, motion_line.normal); + /* In order to be conservative and for simplicity, we use the tiles bounding circles. + * Consider that both the tile and the line have bounding radius of M_SQRT1_2. */ + return abs(dist) < M_SQRT2; +} + +void main() +{ + ivec2 src_tile = ivec2(gl_GlobalInvocationID.xy); + if (any(greaterThanEqual(src_tile, imageSize(in_tiles_img)))) { + return; + } + + vec4 max_motion = imageLoad(in_tiles_img, src_tile); + + MotionPayload payload_prv = motion_blur_tile_indirection_pack_payload(max_motion.xy, src_tile); + MotionPayload payload_nxt = motion_blur_tile_indirection_pack_payload(max_motion.zw, src_tile); + if (true) { + /* Rectangular area (in tiles) where the motion vector spreads. */ + MotionRect motion_rect = compute_motion_rect(src_tile, max_motion.xy); + MotionLine motion_line = compute_motion_line(src_tile, max_motion.xy); + /* Do a conservative rasterization of the line of the motion vector line. */ + for (int x = 0; x < motion_rect.extent.x; x++) { + for (int y = 0; y < motion_rect.extent.y; y++) { + ivec2 tile = motion_rect.bottom_left + ivec2(x, y); + if (is_inside_motion_line(tile, motion_line)) { + motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_PREV, tile, payload_prv); + /* FIXME: This is a bit weird, but for some reason, we need the store the same vector in + * the motion next so that weighting in gather pass is better. */ + motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_NEXT, tile, payload_nxt); + } + } + } + } + + if (true) { + MotionPayload payload = motion_blur_tile_indirection_pack_payload(max_motion.zw, src_tile); + /* Rectangular area (in tiles) where the motion vector spreads. */ + MotionRect motion_rect = compute_motion_rect(src_tile, max_motion.zw); + MotionLine motion_line = compute_motion_line(src_tile, max_motion.zw); + /* Do a conservative rasterization of the line of the motion vector line. */ + for (int x = 0; x < motion_rect.extent.x; x++) { + for (int y = 0; y < motion_rect.extent.y; y++) { + ivec2 tile = motion_rect.bottom_left + ivec2(x, y); + if (is_inside_motion_line(tile, motion_line)) { + motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_NEXT, tile, payload_nxt); + /* FIXME: This is a bit weird, but for some reason, we need the store the same vector in + * the motion next so that weighting in gather pass is better. */ + motion_blur_tile_indirection_store(tile_indirection_buf, MOTION_PREV, tile, payload_prv); + } + } + } + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl new file mode 100644 index 00000000000..cbbeea25d20 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_flatten_comp.glsl @@ -0,0 +1,103 @@ + +/** + * Shaders that down-sample velocity buffer into squared tile of MB_TILE_DIVISOR pixels wide. + * Outputs the largest motion vector in the tile area. + * Also perform velocity resolve to speedup the convolution pass. + * + * Based on: + * A Fast and Stable Feature-Aware Motion Blur Filter + * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai + * + * Adapted from G3D Innovation Engine implementation. + */ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) + +shared uint payload_prev; +shared uint payload_next; +shared vec2 max_motion_prev; +shared vec2 max_motion_next; + +/* Store velocity magnitude in the MSB and thread id in the LSB. */ +uint pack_payload(vec2 motion, uvec2 thread_id) +{ + /* NOTE: We clamp max velocity to 16k pixels. */ + return (min(uint(ceil(length(motion))), 0xFFFFu) << 16u) | (thread_id.y << 8) | thread_id.x; +} + +/* Return thread index from the payload. */ +uvec2 unpack_payload(uint payload) +{ + return uvec2(payload & 0xFFu, (payload >> 8) & 0xFFu); +} + +void main() +{ + if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) { + payload_prev = 0u; + payload_next = 0u; + } + barrier(); + + uint local_payload_prev = 0u; + uint local_payload_next = 0u; + vec2 local_max_motion_prev; + vec2 local_max_motion_next; + + ivec2 texel = min(ivec2(gl_GlobalInvocationID.xy), imageSize(velocity_img) - 1); + + vec2 render_size = vec2(imageSize(velocity_img).xy); + vec2 uv = (vec2(texel) + 0.5) / render_size; + float depth = texelFetch(depth_tx, texel, 0).r; + vec4 motion = velocity_resolve(imageLoad(velocity_img, texel), uv, depth); +#ifdef FLATTEN_VIEWPORT + /* imageLoad does not perform the swizzling like sampler does. Do it manually. */ + motion = motion.xyxy; +#endif + + /* Store resolved velocity to speedup the gather pass. Out of bounds writes are ignored. + * Unfortunately, we cannot convert to pixel space here since it is also used by TAA and the + * motion blur needs to remain optional. */ + imageStore(velocity_img, ivec2(gl_GlobalInvocationID.xy), velocity_pack(motion)); + /* Clip velocity to viewport bounds (in NDC space). */ + vec2 line_clip; + line_clip.x = line_unit_square_intersect_dist_safe(uv * 2.0 - 1.0, motion.xy * 2.0); + line_clip.y = line_unit_square_intersect_dist_safe(uv * 2.0 - 1.0, -motion.zw * 2.0); + motion *= min(line_clip, vec2(1.0)).xxyy; + /* Convert to pixel space. Note this is only for velocity tiles. */ + motion *= render_size.xyxy; + /* Rescale to shutter relative motion for viewport. */ + motion *= motion_blur_buf.motion_scale.xxyy; + + uint sample_payload_prev = pack_payload(motion.xy, gl_LocalInvocationID.xy); + if (local_payload_prev < sample_payload_prev) { + local_payload_prev = sample_payload_prev; + local_max_motion_prev = motion.xy; + } + + uint sample_payload_next = pack_payload(motion.zw, gl_LocalInvocationID.xy); + if (local_payload_next < sample_payload_next) { + local_payload_next = sample_payload_next; + local_max_motion_next = motion.zw; + } + + /* Compare the local payload with the other threads. */ + atomicMax(payload_prev, local_payload_prev); + atomicMax(payload_next, local_payload_next); + barrier(); + + /* Need to broadcast the result to another thread in order to issue a unique write. */ + if (all(equal(unpack_payload(payload_prev), gl_LocalInvocationID.xy))) { + max_motion_prev = local_max_motion_prev; + } + if (all(equal(unpack_payload(payload_next), gl_LocalInvocationID.xy))) { + max_motion_next = local_max_motion_next; + } + barrier(); + + if (all(equal(gl_LocalInvocationID.xy, uvec2(0)))) { + ivec2 tile_co = ivec2(gl_WorkGroupID.xy); + imageStore(out_tiles_img, tile_co, vec4(max_motion_prev, max_motion_next)); + } +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl new file mode 100644 index 00000000000..5249e6637b6 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_gather_comp.glsl @@ -0,0 +1,221 @@ + +/** + * Perform two gather blur in the 2 motion blur directions + * Based on: + * A Fast and Stable Feature-Aware Motion Blur Filter + * by Jean-Philippe Guertin, Morgan McGuire, Derek Nowrouzezahrai + * + * With modification from the presentation: + * Next Generation Post Processing in Call of Duty Advanced Warfare + * by Jorge Jimenez + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_velocity_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_motion_blur_lib.glsl) + +const int gather_sample_count = 8; + +/* Converts uv velocity into pixel space. Assumes velocity_tx is the same resolution as the + * target post-fx framebuffer. */ +vec4 motion_blur_sample_velocity(sampler2D velocity_tx, vec2 uv) +{ + /* We can load velocity without velocity_resolve() since we resolved during the flatten pass. */ + vec4 velocity = velocity_unpack(texture(velocity_tx, uv)); + return velocity * vec2(textureSize(velocity_tx, 0)).xyxy * motion_blur_buf.motion_scale.xxyy; +} + +vec2 spread_compare(float center_motion_length, float sample_motion_length, float offset_length) +{ + return saturate(vec2(center_motion_length, sample_motion_length) - offset_length + 1.0); +} + +vec2 depth_compare(float center_depth, float sample_depth) +{ + vec2 depth_scale = vec2(-motion_blur_buf.depth_scale, motion_blur_buf.depth_scale); + return saturate(0.5 + depth_scale * (sample_depth - center_depth)); +} + +/* Kill contribution if not going the same direction. */ +float dir_compare(vec2 offset, vec2 sample_motion, float sample_motion_length) +{ + if (sample_motion_length < 0.5) { + return 1.0; + } + return (dot(offset, sample_motion) > 0.0) ? 1.0 : 0.0; +} + +/* Return background (x) and foreground (y) weights. */ +vec2 sample_weights(float center_depth, + float sample_depth, + float center_motion_length, + float sample_motion_length, + float offset_length) +{ + /* Classify foreground/background. */ + vec2 depth_weight = depth_compare(center_depth, sample_depth); + /* Weight if sample is overlapping or under the center pixel. */ + vec2 spread_weight = spread_compare(center_motion_length, sample_motion_length, offset_length); + return depth_weight * spread_weight; +} + +struct Accumulator { + vec4 fg; + vec4 bg; + /** x: Background, y: Foreground, z: dir. */ + vec3 weight; +}; + +void gather_sample(vec2 screen_uv, + float center_depth, + float center_motion_len, + vec2 offset, + float offset_len, + const bool next, + inout Accumulator accum) +{ + vec2 sample_uv = screen_uv - offset * motion_blur_buf.target_size_inv; + vec4 sample_vectors = motion_blur_sample_velocity(velocity_tx, sample_uv); + vec2 sample_motion = (next) ? sample_vectors.zw : sample_vectors.xy; + float sample_motion_len = length(sample_motion); + float sample_depth = texture(depth_tx, sample_uv).r; + vec4 sample_color = textureLod(in_color_tx, sample_uv, 0.0); + + sample_depth = get_view_z_from_depth(sample_depth); + + vec3 weights; + weights.xy = sample_weights( + center_depth, sample_depth, center_motion_len, sample_motion_len, offset_len); + weights.z = dir_compare(offset, sample_motion, sample_motion_len); + weights.xy *= weights.z; + + accum.fg += sample_color * weights.y; + accum.bg += sample_color * weights.x; + accum.weight += weights; +} + +void gather_blur(vec2 screen_uv, + vec2 center_motion, + float center_depth, + vec2 max_motion, + float ofs, + const bool next, + inout Accumulator accum) +{ + float center_motion_len = length(center_motion); + float max_motion_len = length(max_motion); + + /* Tile boundaries randomization can fetch a tile where there is less motion than this pixel. + * Fix this by overriding the max_motion. */ + if (max_motion_len < center_motion_len) { + max_motion_len = center_motion_len; + max_motion = center_motion; + } + + if (max_motion_len < 0.5) { + return; + } + + int i; + float t, inc = 1.0 / float(gather_sample_count); + for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) { + gather_sample(screen_uv, + center_depth, + center_motion_len, + max_motion * t, + max_motion_len * t, + next, + accum); + } + + if (center_motion_len < 0.5) { + return; + } + + for (i = 0, t = ofs * inc; i < gather_sample_count; i++, t += inc) { + /* Also sample in center motion direction. + * Allow recovering motion where there is conflicting + * motion between foreground and background. */ + gather_sample(screen_uv, + center_depth, + center_motion_len, + center_motion * t, + center_motion_len * t, + next, + accum); + } +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(depth_tx, 0).xy); + + if (!in_texture_range(texel, depth_tx)) { + return; + } + + /* Data of the center pixel of the gather (target). */ + float center_depth = get_view_z_from_depth(texelFetch(depth_tx, texel, 0).r); + vec4 center_motion = motion_blur_sample_velocity(velocity_tx, uv); + + vec4 center_color = textureLod(in_color_tx, uv, 0.0); + + float noise_offset = sampling_rng_1D_get(SAMPLING_TIME); + /** TODO(fclem) Blue noise. */ + vec2 rand = vec2(interlieved_gradient_noise(vec2(gl_GlobalInvocationID.xy), 0, noise_offset), + interlieved_gradient_noise(vec2(gl_GlobalInvocationID.xy), 1, noise_offset)); + + /* Randomize tile boundary to avoid ugly discontinuities. Randomize 1/4th of the tile. + * Note this randomize only in one direction but in practice it's enough. */ + rand.x = rand.x * 2.0 - 1.0; + ivec2 tile = (texel + ivec2(rand.x * float(MOTION_BLUR_TILE_SIZE) * 0.25)) / + MOTION_BLUR_TILE_SIZE; + tile = clamp(tile, ivec2(0), imageSize(in_tiles_img) - 1); + /* NOTE: Tile velocity is already in pixel space and with correct zw sign. */ + vec4 max_motion; + /* Load dilation result from the indirection table. */ + ivec2 tile_prev; + motion_blur_tile_indirection_load(tile_indirection_buf, MOTION_PREV, tile, tile_prev); + max_motion.xy = imageLoad(in_tiles_img, tile_prev).xy; + ivec2 tile_next; + motion_blur_tile_indirection_load(tile_indirection_buf, MOTION_NEXT, tile, tile_next); + max_motion.zw = imageLoad(in_tiles_img, tile_next).zw; + + Accumulator accum; + accum.weight = vec3(0.0, 0.0, 1.0); + accum.bg = vec4(0.0); + accum.fg = vec4(0.0); + /* First linear gather. time = [T - delta, T] */ + gather_blur(uv, center_motion.xy, center_depth, max_motion.xy, rand.y, false, accum); + /* Second linear gather. time = [T, T + delta] */ + gather_blur(uv, center_motion.zw, center_depth, max_motion.zw, rand.y, true, accum); + +#if 1 /* Own addition. Not present in reference implementation. */ + /* Avoid division by 0.0. */ + float w = 1.0 / (50.0 * float(gather_sample_count) * 4.0); + accum.bg += center_color * w; + accum.weight.x += w; + /* NOTE: In Jimenez's presentation, they used center sample. + * We use background color as it contains more information for foreground + * elements that have not enough weights. + * Yield better blur in complex motion. */ + center_color = accum.bg / accum.weight.x; +#endif + /* Merge background. */ + accum.fg += accum.bg; + accum.weight.y += accum.weight.x; + /* Balance accumulation for failed samples. + * We replace the missing foreground by the background. */ + float blend_fac = saturate(1.0 - accum.weight.y / accum.weight.z); + vec4 out_color = (accum.fg / accum.weight.z) + center_color * blend_fac; + +#if 0 /* For debugging. */ + out_color.rgb = out_color.ggg; + out_color.rg += max_motion.xy; +#endif + + imageStore(out_color_img, texel, out_color); +} diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl new file mode 100644 index 00000000000..436fd01795a --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_motion_blur_lib.glsl @@ -0,0 +1,48 @@ + + +/* -------------------------------------------------------------------- */ +/** \name Tile indirection packing + * \{ */ + +#define MotionPayload uint + +/* Store velocity magnitude in the MSB to be able to use it with atomicMax operations. */ +MotionPayload motion_blur_tile_indirection_pack_payload(vec2 motion, uvec2 payload) +{ + /* NOTE: Clamp to 16383 pixel velocity. After that, it is tile position that determine the tile + * to dilate over. */ + uint velocity = min(uint(ceil(length(motion))), 0x3FFFu); + /* Designed for 512x512 tiles max. */ + return (velocity << 18u) | ((payload.x & 0x1FFu) << 9u) | (payload.y & 0x1FFu); +} + +/* Return thread index. */ +ivec2 motion_blur_tile_indirection_pack_payload(uint data) +{ + return ivec2((data >> 9u) & 0x1FFu, data & 0x1FFu); +} + +uint motion_blur_tile_indirection_index(uint motion_step, uvec2 tile) +{ + uint index = tile.x; + index += tile.y * MOTION_BLUR_MAX_TILE; + index += motion_step * MOTION_BLUR_MAX_TILE * MOTION_BLUR_MAX_TILE; + return index; +} + +#define MOTION_PREV 0u +#define MOTION_NEXT 1u + +#define motion_blur_tile_indirection_store(table_, step_, tile, payload_) \ + if (true) { \ + uint index = motion_blur_tile_indirection_index(step_, tile); \ + atomicMax(table_[index], payload_); \ + } + +#define motion_blur_tile_indirection_load(table_, step_, tile_, result_) \ + if (true) { \ + uint index = motion_blur_tile_indirection_index(step_, tile_); \ + result_ = motion_blur_tile_indirection_pack_payload(table_[index]); \ + } + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl index c488216eeac..13ad387289d 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_nodetree_lib.glsl @@ -39,6 +39,8 @@ bool closure_select(float weight, inout float total_weight, inout float r) destination = candidate; \ } +float g_closure_rand; + void closure_weights_reset() { g_diffuse_data.weight = 0.0; @@ -58,18 +60,8 @@ void closure_weights_reset() g_refraction_data.roughness = 0.0; g_refraction_data.ior = 0.0; - /* TEMP */ -#define P(x) ((x + 0.5) / 16.0) - const vec4 dither_mat4x4[4] = vec4[4](vec4(P(0.0), P(8.0), P(2.0), P(10.0)), - vec4(P(12.0), P(4.0), P(14.0), P(6.0)), - vec4(P(3.0), P(11.0), P(1.0), P(9.0)), - vec4(P(15.0), P(7.0), P(13.0), P(5.0))); -#undef P #if defined(GPU_FRAGMENT_SHADER) - ivec2 pix = ivec2(gl_FragCoord.xy) % ivec2(4); - g_diffuse_rand = dither_mat4x4[pix.x][pix.y]; - g_reflection_rand = dither_mat4x4[pix.x][pix.y]; - g_refraction_rand = dither_mat4x4[pix.x][pix.y]; + g_diffuse_rand = g_reflection_rand = g_refraction_rand = g_closure_rand; #else g_diffuse_rand = 0.0; g_reflection_rand = 0.0; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl new file mode 100644 index 00000000000..0eea4a5ff33 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_sampling_lib.glsl @@ -0,0 +1,104 @@ + +/** + * Sampling data accessors and random number generators. + * Also contains some sample mapping functions. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +/* -------------------------------------------------------------------- */ +/** \name Sampling data. + * + * Return a random values from Low Discrepancy Sequence in [0..1) range. + * This value is uniform (constant) for the whole scene sample. + * You might want to couple it with a noise function. + * \{ */ + +#ifdef EEVEE_SAMPLING_DATA + +float sampling_rng_1D_get(const eSamplingDimension dimension) +{ + return sampling_buf.dimensions[dimension]; +} + +vec2 sampling_rng_2D_get(const eSamplingDimension dimension) +{ + return vec2(sampling_buf.dimensions[dimension], sampling_buf.dimensions[dimension + 1u]); +} + +vec3 sampling_rng_3D_get(const eSamplingDimension dimension) +{ + return vec3(sampling_buf.dimensions[dimension], + sampling_buf.dimensions[dimension + 1u], + sampling_buf.dimensions[dimension + 2u]); +} + +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Random Number Generators. + * \{ */ + +/* Interlieved gradient noise by Jorge Jimenez + * http://www.iryoku.com/next-generation-post-processing-in-call-of-duty-advanced-warfare + * Seeding found by Epic Game. */ +float interlieved_gradient_noise(vec2 pixel, float seed, float offset) +{ + pixel += seed * (vec2(47, 17) * 0.695); + return fract(offset + 52.9829189 * fract(0.06711056 * pixel.x + 0.00583715 * pixel.y)); +} + +/* From: http://holger.dammertz.org/stuff/notes_HammersleyOnHemisphere.html */ +float van_der_corput_radical_inverse(uint bits) +{ +#if 0 /* Reference */ + bits = (bits << 16u) | (bits >> 16u); + bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); + bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); + bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); + bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); +#else + bits = bitfieldReverse(bits); +#endif + /* Same as dividing by 0x100000000. */ + return float(bits) * 2.3283064365386963e-10; +} + +vec2 hammersley_2d(float i, float sample_count) +{ + vec2 rand; + rand.x = i / sample_count; + rand.y = van_der_corput_radical_inverse(uint(i)); + return rand; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Distribution mapping. + * + * Functions mapping input random numbers to sampling shapes (i.e: hemisphere). + * \{ */ + +/* Given 2 random number in [0..1] range, return a random unit disk sample. */ +vec2 sample_disk(vec2 noise) +{ + float angle = noise.x * M_2PI; + return vec2(cos(angle), sin(angle)) * sqrt(noise.y); +} + +/* This transform a 2d random sample (in [0..1] range) to a sample located on a cylinder of the + * same range. This is because the sampling functions expect such a random sample which is + * normally precomputed. */ +vec3 sample_cylinder(vec2 rand) +{ + float theta = rand.x; + float phi = (rand.y - 0.5) * M_2PI; + float cos_phi = cos(phi); + float sin_phi = sqrt(1.0 - sqr(cos_phi)) * sign(phi); + return vec3(theta, cos_phi, sin_phi); +} + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl index 34ea288852a..bd32215ddc2 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_depth_frag.glsl @@ -73,7 +73,7 @@ void main() nodetree_surface(); - // float noise_offset = sampling_rng_1D_get(sampling_buf, SAMPLING_TRANSPARENCY); + // float noise_offset = sampling_rng_1D_get(SAMPLING_TRANSPARENCY); float noise_offset = 0.5; float random_threshold = hashed_alpha_threshold(1.0, noise_offset, g_data.P); @@ -84,7 +84,7 @@ void main() #endif #ifdef MAT_VELOCITY - out_velocity = velocity_surface(interp.P + motion.prev, interp.P, interp.P - motion.next); + out_velocity = velocity_surface(interp.P + motion.prev, interp.P, interp.P + motion.next); out_velocity = velocity_pack(out_velocity); #endif } diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl index 48ced4e5374..26d2c066937 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_surf_forward_frag.glsl @@ -5,35 +5,36 @@ * This is used by alpha blended materials and materials using Shader to RGB nodes. **/ -#pragma BLENDER_REQUIRE(common_view_lib.glsl) -#pragma BLENDER_REQUIRE(common_math_lib.glsl) #pragma BLENDER_REQUIRE(common_hair_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_lib.glsl) +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_light_eval_lib.glsl) #pragma BLENDER_REQUIRE(eevee_nodetree_lib.glsl) +#pragma BLENDER_REQUIRE(eevee_sampling_lib.glsl) #pragma BLENDER_REQUIRE(eevee_surf_lib.glsl) -float spec_light(ClosureReflection ref) -{ - float gloss = saturate(1.0 - ref.roughness); - float shininess = exp2(10.0 * gloss + 1.0); - vec3 N = ref.N; - vec3 L = vec3(0.0, 0.0, 1.0); - vec3 H = normalize(L + cameraVec(g_data.P)); - float spec_angle = saturate(dot(N, H)); - float normalization_factor = shininess * 0.125 + 1.0; - float spec_light = pow(spec_angle, shininess) * saturate(dot(N, L)) * normalization_factor; - return spec_light; -} - vec4 closure_to_rgba(Closure cl) { + vec3 diffuse_light = vec3(0.0); + vec3 reflection_light = vec3(0.0); + vec3 refraction_light = vec3(0.0); + + float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos); + + light_eval(g_diffuse_data, + g_reflection_data, + g_data.P, + cameraVec(g_data.P), + vP_z, + 0.01 /* TODO(fclem) thickness. */, + diffuse_light, + reflection_light); + vec4 out_color; out_color.rgb = g_emission; - out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * - saturate(g_diffuse_data.N.z * 0.5 + 0.5); - out_color.rgb += g_reflection_data.color * g_reflection_data.weight * - spec_light(g_reflection_data); - out_color.rgb += g_refraction_data.color * g_refraction_data.weight * - saturate(g_refraction_data.N.z * 0.5 + 0.5); + out_color.rgb += g_diffuse_data.color * g_diffuse_data.weight * diffuse_light; + out_color.rgb += g_reflection_data.color * g_reflection_data.weight * reflection_light; + out_color.rgb += g_refraction_data.color * g_refraction_data.weight * refraction_light; out_color.a = saturate(1.0 - avg(g_transmittance)); @@ -47,15 +48,29 @@ void main() { init_globals(); + float noise = utility_tx_fetch(utility_tx, gl_FragCoord.xy, UTIL_BLUE_NOISE_LAYER).r; + g_closure_rand = fract(noise + sampling_rng_1D_get(SAMPLING_CLOSURE)); + fragment_displacement(); nodetree_surface(); g_holdout = saturate(g_holdout); - vec3 diffuse_light = vec3(saturate(g_diffuse_data.N.z * 0.5 + 0.5)); - vec3 reflection_light = vec3(spec_light(g_reflection_data)); - vec3 refraction_light = vec3(saturate(g_refraction_data.N.z * 0.5 + 0.5)); + vec3 diffuse_light = vec3(0.0); + vec3 reflection_light = vec3(0.0); + vec3 refraction_light = vec3(0.0); + + float vP_z = dot(cameraForward, g_data.P) - dot(cameraForward, cameraPos); + + light_eval(g_diffuse_data, + g_reflection_data, + g_data.P, + cameraVec(g_data.P), + vP_z, + 0.01 /* TODO(fclem) thickness. */, + diffuse_light, + reflection_light); g_diffuse_data.color *= g_diffuse_data.weight; g_reflection_data.color *= g_reflection_data.weight; diff --git a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl index c21456b7a5c..8d02609fedc 100644 --- a/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl +++ b/source/blender/draw/engines/eevee_next/shaders/eevee_velocity_lib.glsl @@ -2,8 +2,6 @@ #pragma BLENDER_REQUIRE(common_view_lib.glsl) #pragma BLENDER_REQUIRE(eevee_camera_lib.glsl) -#ifdef VELOCITY_CAMERA - vec4 velocity_pack(vec4 data) { return data * 0.01; @@ -14,6 +12,8 @@ vec4 velocity_unpack(vec4 data) return data * 100.0; } +#ifdef VELOCITY_CAMERA + /** * Given a triple of position, compute the previous and next motion vectors. * Returns uv space motion vectors in pairs (motion_prev.xy, motion_next.xy). @@ -24,7 +24,15 @@ vec4 velocity_surface(vec3 P_prv, vec3 P, vec3 P_nxt) vec2 prev_uv = project_point(camera_prev.persmat, P_prv).xy; vec2 curr_uv = project_point(camera_curr.persmat, P).xy; vec2 next_uv = project_point(camera_next.persmat, P_nxt).xy; - + /* Fix issue with perspective division. */ + if (any(isnan(prev_uv))) { + prev_uv = curr_uv; + } + if (any(isnan(next_uv))) { + next_uv = curr_uv; + } + /* NOTE: We output both vectors in the same direction so we can reuse the same vector + * with rgrg swizzle in viewport. */ vec4 motion = vec4(prev_uv - curr_uv, curr_uv - next_uv); /* Convert NDC velocity to UV velocity */ motion *= 0.5; @@ -39,13 +47,14 @@ vec4 velocity_surface(vec3 P_prv, vec3 P, vec3 P_nxt) */ vec4 velocity_background(vec3 vV) { - /* Only transform direction to avoid loosing precision. */ + /* Only transform direction to avoid losing precision. */ vec3 V = transform_direction(camera_curr.viewinv, vV); /* NOTE: We don't use the drw_view.winmat to avoid adding the TAA jitter to the velocity. */ vec2 prev_uv = project_point(camera_prev.winmat, V).xy; vec2 curr_uv = project_point(camera_curr.winmat, V).xy; vec2 next_uv = project_point(camera_next.winmat, V).xy; - + /* NOTE: We output both vectors in the same direction so we can reuse the same vector + * with rgrg swizzle in viewport. */ vec4 motion = vec4(prev_uv - curr_uv, curr_uv - next_uv); /* Convert NDC velocity to UV velocity */ motion *= 0.5; @@ -53,15 +62,8 @@ vec4 velocity_background(vec3 vV) return motion; } -/** - * Load and resolve correct velocity as some pixels might still not have correct - * motion data for performance reasons. - */ -vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth) +vec4 velocity_resolve(vec4 vector, vec2 uv, float depth) { - vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(vector_tx, 0).xy); - vec4 vector = texelFetch(vector_tx, texel, 0); - if (vector.x == VELOCITY_INVALID) { bool is_background = (depth == 1.0); if (is_background) { @@ -78,6 +80,18 @@ vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth) return velocity_unpack(vector); } +/** + * Load and resolve correct velocity as some pixels might still not have correct + * motion data for performance reasons. + * Returns motion vector in render UV space. + */ +vec4 velocity_resolve(sampler2D vector_tx, ivec2 texel, float depth) +{ + vec2 uv = (vec2(texel) + 0.5) / vec2(textureSize(vector_tx, 0).xy); + vec4 vector = texelFetch(vector_tx, texel, 0); + return velocity_resolve(vector, uv, depth); +} + #endif #ifdef MAT_VELOCITY diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh new file mode 100644 index 00000000000..b398a6cc4e7 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh @@ -0,0 +1,247 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "eevee_defines.hh" +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Setup + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_bokeh_lut) + .do_static_compilation(true) + .local_group_size(DOF_BOKEH_LUT_SIZE, DOF_BOKEH_LUT_SIZE) + .additional_info("eevee_shared", "draw_view") + .uniform_buf(1, "DepthOfFieldData", "dof_buf") + .image(0, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_gather_lut_img") + .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_scatter_lut_img") + .image(2, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_resolve_lut_img") + .compute_source("eevee_depth_of_field_bokeh_lut_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_setup) + .do_static_compilation(true) + .local_group_size(DOF_DEFAULT_GROUP_SIZE, DOF_DEFAULT_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view") + .uniform_buf(1, "DepthOfFieldData", "dof_buf") + .sampler(0, ImageType::FLOAT_2D, "color_tx") + .sampler(1, ImageType::DEPTH_2D, "depth_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") + .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img") + .compute_source("eevee_depth_of_field_setup_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_stabilize) + .do_static_compilation(true) + .local_group_size(DOF_STABILIZE_GROUP_SIZE, DOF_STABILIZE_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view", "eevee_velocity_camera") + .uniform_buf(4, "DepthOfFieldData", "dof_buf") + .sampler(0, ImageType::FLOAT_2D, "coc_tx") + .sampler(1, ImageType::FLOAT_2D, "color_tx") + .sampler(2, ImageType::FLOAT_2D, "velocity_tx") + .sampler(3, ImageType::FLOAT_2D, "in_history_tx") + .sampler(4, ImageType::DEPTH_2D, "depth_tx") + .push_constant(Type::BOOL, "use_history") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") + .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_img") + .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_history_img") + .compute_source("eevee_depth_of_field_stabilize_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_downsample) + .do_static_compilation(true) + .local_group_size(DOF_DEFAULT_GROUP_SIZE, DOF_DEFAULT_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view") + .sampler(0, ImageType::FLOAT_2D, "color_tx") + .sampler(1, ImageType::FLOAT_2D, "coc_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") + .compute_source("eevee_depth_of_field_downsample_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_reduce) + .do_static_compilation(true) + .local_group_size(DOF_REDUCE_GROUP_SIZE, DOF_REDUCE_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view") + .uniform_buf(1, "DepthOfFieldData", "dof_buf") + .sampler(0, ImageType::FLOAT_2D, "downsample_tx") + .storage_buf(0, Qualifier::WRITE, "ScatterRect", "scatter_fg_list_buf[]") + .storage_buf(1, Qualifier::WRITE, "ScatterRect", "scatter_bg_list_buf[]") + .storage_buf(2, Qualifier::READ_WRITE, "DrawCommand", "scatter_fg_indirect_buf") + .storage_buf(3, Qualifier::READ_WRITE, "DrawCommand", "scatter_bg_indirect_buf") + .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "inout_color_lod0_img") + .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_lod1_img") + .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_lod2_img") + .image(3, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_lod3_img") + .image(4, GPU_R16F, Qualifier::READ, ImageType::FLOAT_2D, "in_coc_lod0_img") + .image(5, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_lod1_img") + .image(6, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_lod2_img") + .image(7, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_coc_lod3_img") + .compute_source("eevee_depth_of_field_reduce_comp.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Circle-Of-Confusion Tiles + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_flatten) + .do_static_compilation(true) + .local_group_size(DOF_TILES_FLATTEN_GROUP_SIZE, DOF_TILES_FLATTEN_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view") + .sampler(0, ImageType::FLOAT_2D, "coc_tx") + .image(2, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_fg_img") + .image(3, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_bg_img") + .compute_source("eevee_depth_of_field_tiles_flatten_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate) + .additional_info("eevee_shared", "draw_view", "eevee_depth_of_field_tiles_common") + .local_group_size(DOF_TILES_DILATE_GROUP_SIZE, DOF_TILES_DILATE_GROUP_SIZE) + .image(2, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_fg_img") + .image(3, GPU_R11F_G11F_B10F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_bg_img") + .push_constant(Type::INT, "ring_count") + .push_constant(Type::INT, "ring_width_multiplier") + .compute_source("eevee_depth_of_field_tiles_dilate_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate_minabs) + .do_static_compilation(true) + .define("DILATE_MODE_MIN_MAX", "false") + .additional_info("eevee_depth_of_field_tiles_dilate"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_dilate_minmax) + .do_static_compilation(true) + .define("DILATE_MODE_MIN_MAX", "true") + .additional_info("eevee_depth_of_field_tiles_dilate"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_tiles_common) + .image(0, GPU_R11F_G11F_B10F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_fg_img") + .image(1, GPU_R11F_G11F_B10F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_bg_img"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Variations + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_no_lut) + .define("DOF_BOKEH_TEXTURE", "false") + /** + * WORKAROUND(@fclem): This is to keep the code as is for now. The bokeh_lut_tx is referenced + * even if not used after optimization. But we don't want to include it in the create infos. + */ + .define("bokeh_lut_tx", "color_tx"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_lut) + .define("DOF_BOKEH_TEXTURE", "true") + .sampler(5, ImageType::FLOAT_2D, "bokeh_lut_tx"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_background).define("DOF_FOREGROUND_PASS", "false"); +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_foreground).define("DOF_FOREGROUND_PASS", "true"); + +#define EEVEE_DOF_FINAL_VARIATION(name, ...) \ + GPU_SHADER_CREATE_INFO(name).additional_info(__VA_ARGS__).do_static_compilation(true); + +#define EEVEE_DOF_LUT_VARIATIONS(prefix, ...) \ + EEVEE_DOF_FINAL_VARIATION(prefix##_lut, "eevee_depth_of_field_lut", __VA_ARGS__) \ + EEVEE_DOF_FINAL_VARIATION(prefix##_no_lut, "eevee_depth_of_field_no_lut", __VA_ARGS__) + +#define EEVEE_DOF_GROUND_VARIATIONS(name, ...) \ + EEVEE_DOF_LUT_VARIATIONS(name##_background, "eevee_depth_of_field_background", __VA_ARGS__) \ + EEVEE_DOF_LUT_VARIATIONS(name##_foreground, "eevee_depth_of_field_foreground", __VA_ARGS__) + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Gather + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_gather_common) + .additional_info("eevee_shared", + "draw_view", + "eevee_depth_of_field_tiles_common", + "eevee_sampling_data") + .uniform_buf(2, "DepthOfFieldData", "dof_buf") + .local_group_size(DOF_GATHER_GROUP_SIZE, DOF_GATHER_GROUP_SIZE) + .sampler(0, ImageType::FLOAT_2D, "color_tx") + .sampler(1, ImageType::FLOAT_2D, "color_bilinear_tx") + .sampler(2, ImageType::FLOAT_2D, "coc_tx") + .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") + .image(3, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_weight_img"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_gather) + .image(4, GPU_RG16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_occlusion_img") + .compute_source("eevee_depth_of_field_gather_comp.glsl") + .additional_info("eevee_depth_of_field_gather_common"); + +EEVEE_DOF_GROUND_VARIATIONS(eevee_depth_of_field_gather, "eevee_depth_of_field_gather") + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_hole_fill) + .do_static_compilation(true) + .compute_source("eevee_depth_of_field_hole_fill_comp.glsl") + .additional_info("eevee_depth_of_field_gather_common", "eevee_depth_of_field_no_lut"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_filter) + .do_static_compilation(true) + .local_group_size(DOF_FILTER_GROUP_SIZE, DOF_FILTER_GROUP_SIZE) + .additional_info("eevee_shared") + .sampler(0, ImageType::FLOAT_2D, "color_tx") + .sampler(1, ImageType::FLOAT_2D, "weight_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") + .image(1, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_weight_img") + .compute_source("eevee_depth_of_field_filter_comp.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Scatter + * \{ */ + +GPU_SHADER_INTERFACE_INFO(eevee_depth_of_field_scatter_iface, "interp") + /** Colors, weights, and Circle of confusion radii for the 4 pixels to scatter. */ + .flat(Type::VEC4, "color_and_coc1") + .flat(Type::VEC4, "color_and_coc2") + .flat(Type::VEC4, "color_and_coc3") + .flat(Type::VEC4, "color_and_coc4") + /** Sprite pixel position with origin at sprite center. In pixels. */ + .no_perspective(Type::VEC2, "rect_uv1") + .no_perspective(Type::VEC2, "rect_uv2") + .no_perspective(Type::VEC2, "rect_uv3") + .no_perspective(Type::VEC2, "rect_uv4") + /** Scaling factor for the bokeh distance. */ + .flat(Type::FLOAT, "distance_scale"); + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_scatter) + .do_static_compilation(true) + .additional_info("eevee_shared", "draw_view") + .sampler(0, ImageType::FLOAT_2D, "occlusion_tx") + .sampler(1, ImageType::FLOAT_2D, "bokeh_lut_tx") + .storage_buf(0, Qualifier::READ, "ScatterRect", "scatter_list_buf[]") + .fragment_out(0, Type::VEC4, "out_color") + .push_constant(Type::BOOL, "use_bokeh_lut") + .vertex_out(eevee_depth_of_field_scatter_iface) + .vertex_source("eevee_depth_of_field_scatter_vert.glsl") + .fragment_source("eevee_depth_of_field_scatter_frag.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Resolve + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_depth_of_field_resolve) + .define("DOF_RESOLVE_PASS", "true") + .local_group_size(DOF_RESOLVE_GROUP_SIZE, DOF_RESOLVE_GROUP_SIZE) + .additional_info("eevee_shared", + "draw_view", + "eevee_depth_of_field_tiles_common", + "eevee_sampling_data") + .uniform_buf(2, "DepthOfFieldData", "dof_buf") + .sampler(0, ImageType::DEPTH_2D, "depth_tx") + .sampler(1, ImageType::FLOAT_2D, "color_tx") + .sampler(2, ImageType::FLOAT_2D, "color_bg_tx") + .sampler(3, ImageType::FLOAT_2D, "color_fg_tx") + .sampler(4, ImageType::FLOAT_2D, "color_hole_fill_tx") + .sampler(7, ImageType::FLOAT_2D, "weight_bg_tx") + .sampler(8, ImageType::FLOAT_2D, "weight_fg_tx") + .sampler(9, ImageType::FLOAT_2D, "weight_hole_fill_tx") + .sampler(10, ImageType::FLOAT_2D, "stable_color_tx") + .image(2, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") + .compute_source("eevee_depth_of_field_resolve_comp.glsl"); + +EEVEE_DOF_LUT_VARIATIONS(eevee_depth_of_field_resolve, "eevee_depth_of_field_resolve") + +/** \} */ diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh new file mode 100644 index 00000000000..56fda25ed13 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "eevee_defines.hh" +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Shared + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_light_data) + .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf") + .storage_buf(1, Qualifier::READ, "LightData", "light_buf[]") + .storage_buf(2, Qualifier::READ, "uint", "light_zbin_buf[]") + .storage_buf(3, Qualifier::READ, "uint", "light_tile_buf[]"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Culling + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_light_culling_select) + .do_static_compilation(true) + .additional_info("eevee_shared", "draw_view") + .local_group_size(CULLING_SELECT_GROUP_SIZE) + .storage_buf(0, Qualifier::READ_WRITE, "LightCullingData", "light_cull_buf") + .storage_buf(1, Qualifier::READ, "LightData", "in_light_buf[]") + .storage_buf(2, Qualifier::WRITE, "LightData", "out_light_buf[]") + .storage_buf(3, Qualifier::WRITE, "float", "out_zdist_buf[]") + .storage_buf(4, Qualifier::WRITE, "uint", "out_key_buf[]") + .compute_source("eevee_light_culling_select_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_light_culling_sort) + .do_static_compilation(true) + .additional_info("eevee_shared", "draw_view") + .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf") + .storage_buf(1, Qualifier::READ, "LightData", "in_light_buf[]") + .storage_buf(2, Qualifier::WRITE, "LightData", "out_light_buf[]") + .storage_buf(3, Qualifier::READ, "float", "in_zdist_buf[]") + .storage_buf(4, Qualifier::READ, "uint", "in_key_buf[]") + .local_group_size(CULLING_SORT_GROUP_SIZE) + .compute_source("eevee_light_culling_sort_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_light_culling_zbin) + .do_static_compilation(true) + .additional_info("eevee_shared", "draw_view") + .local_group_size(CULLING_ZBIN_GROUP_SIZE) + .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf") + .storage_buf(1, Qualifier::READ, "LightData", "light_buf[]") + .storage_buf(2, Qualifier::WRITE, "uint", "out_zbin_buf[]") + .compute_source("eevee_light_culling_zbin_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_light_culling_tile) + .do_static_compilation(true) + .additional_info("eevee_shared", "draw_view") + .local_group_size(CULLING_TILE_GROUP_SIZE) + .storage_buf(0, Qualifier::READ, "LightCullingData", "light_cull_buf") + .storage_buf(1, Qualifier::READ, "LightData", "light_buf[]") + .storage_buf(2, Qualifier::WRITE, "uint", "out_light_tile_buf[]") + .compute_source("eevee_light_culling_tile_comp.glsl"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug + * \{ */ + +GPU_SHADER_CREATE_INFO(eevee_light_culling_debug) + .do_static_compilation(true) + .sampler(0, ImageType::DEPTH_2D, "depth_tx") + .fragment_out(0, Type::VEC4, "out_debug_color") + .additional_info("eevee_shared", "draw_view") + .fragment_source("eevee_light_culling_debug_frag.glsl") + .additional_info("draw_fullscreen", "eevee_light_data"); + +/** \} */ 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 2368061402c..6929dec1150 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 @@ -12,8 +12,11 @@ GPU_SHADER_CREATE_INFO(eevee_shared) .typedef_source("eevee_shader_shared.hh"); GPU_SHADER_CREATE_INFO(eevee_sampling_data) + .define("EEVEE_SAMPLING_DATA") .additional_info("eevee_shared") - .uniform_buf(14, "SamplingData", "sampling_buf"); + .storage_buf(14, Qualifier::READ, "SamplingData", "sampling_buf"); + +GPU_SHADER_CREATE_INFO(eevee_utility_texture).sampler(8, ImageType::FLOAT_2D_ARRAY, "utility_tx"); /** \} */ @@ -115,14 +118,14 @@ GPU_SHADER_CREATE_INFO(eevee_surf_forward) .image_out(3, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_light_img") .image_out(4, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_specular_color_img") .image_out(5, Qualifier::READ_WRITE, GPU_RGBA16F, "rp_emission_img") - .additional_info("eevee_aov_out" - // "eevee_sampling_data", + .additional_info("eevee_aov_out", + "eevee_light_data", + "eevee_utility_texture", + "eevee_sampling_data" // "eevee_lightprobe_data", /* Optionally added depending on the material. */ // "eevee_raytrace_data", // "eevee_transmittance_data", - // "eevee_utility_texture", - // "eevee_light_data", // "eevee_shadow_data" ); diff --git a/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh new file mode 100644 index 00000000000..d6ff34b0ed2 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "eevee_defines.hh" +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten) + .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view", "eevee_velocity_camera") + .uniform_buf(4, "MotionBlurData", "motion_blur_buf") + .sampler(0, ImageType::DEPTH_2D, "depth_tx") + .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_tiles_img") + .compute_source("eevee_motion_blur_flatten_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_viewport) + .do_static_compilation(true) + .define("FLATTEN_VIEWPORT") + .image(0, GPU_RG16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_img") + .additional_info("eevee_motion_blur_tiles_flatten"); + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_flatten_render) + .do_static_compilation(true) + .image(0, GPU_RGBA16F, Qualifier::READ_WRITE, ImageType::FLOAT_2D, "velocity_img") + .additional_info("eevee_motion_blur_tiles_flatten"); + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_tiles_dilate) + .do_static_compilation(true) + .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE) + .additional_info("eevee_shared") + /* NOTE: See MotionBlurTileIndirection. */ + .storage_buf(0, Qualifier::READ_WRITE, "uint", "tile_indirection_buf[]") + .image(1, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_img") + .compute_source("eevee_motion_blur_dilate_comp.glsl"); + +GPU_SHADER_CREATE_INFO(eevee_motion_blur_gather) + .do_static_compilation(true) + .local_group_size(MOTION_BLUR_GROUP_SIZE, MOTION_BLUR_GROUP_SIZE) + .additional_info("eevee_shared", "draw_view", "eevee_sampling_data") + .uniform_buf(4, "MotionBlurData", "motion_blur_buf") + .sampler(0, ImageType::DEPTH_2D, "depth_tx") + .sampler(1, ImageType::FLOAT_2D, "velocity_tx") + .sampler(2, ImageType::FLOAT_2D, "in_color_tx") + /* NOTE: See MotionBlurTileIndirection. */ + .storage_buf(0, Qualifier::READ, "uint", "tile_indirection_buf[]") + .image(0, GPU_RGBA16F, Qualifier::READ, ImageType::FLOAT_2D, "in_tiles_img") + .image(1, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "out_color_img") + .compute_source("eevee_motion_blur_gather_comp.glsl"); diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 332c7f67c64..2f9d20b3902 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -19,6 +19,8 @@ extern "C" { #endif +#define GP_LIGHT + #include "gpencil_defines.h" #include "gpencil_shader_shared.h" diff --git a/source/blender/draw/engines/gpencil/gpencil_shader_shared.h b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h index 50ff7e7efc7..4c621e955b9 100644 --- a/source/blender/draw/engines/gpencil/gpencil_shader_shared.h +++ b/source/blender/draw/engines/gpencil/gpencil_shader_shared.h @@ -7,7 +7,9 @@ typedef struct gpMaterial gpMaterial; typedef struct gpLight gpLight; typedef enum gpMaterialFlag gpMaterialFlag; +# ifdef GP_LIGHT typedef enum gpLightType gpLightType; +# endif # endif #endif @@ -75,8 +77,9 @@ struct gpMaterial { }; BLI_STATIC_ASSERT_ALIGN(gpMaterial, 16) +#ifdef GP_LIGHT struct gpLight { -#ifndef GPU_SHADER +# ifndef GPU_SHADER float3 color; gpLightType type; float3 right; @@ -87,7 +90,7 @@ struct gpLight { float _pad0; float3 position; float _pad1; -#else +# else /* Some drivers are completely messing the alignment or the fetches here. * We are forced to pack these into vec4 otherwise we only get 0.0 as value. */ /* NOTE(@fclem): This was the case on MacOS OpenGL implementation. @@ -97,17 +100,18 @@ struct gpLight { float4 packed2; float4 packed3; float4 packed4; -# define _color packed0.xyz -# define _type packed0.w -# define _right packed1.xyz -# define _spot_size packed1.w -# define _up packed2.xyz -# define _spot_blend packed2.w -# define _forward packed3.xyz -# define _position packed4.xyz -#endif +# define _color packed0.xyz +# define _type packed0.w +# define _right packed1.xyz +# define _spot_size packed1.w +# define _up packed2.xyz +# define _spot_blend packed2.w +# define _forward packed3.xyz +# define _position packed4.xyz +# endif }; BLI_STATIC_ASSERT_ALIGN(gpLight, 16) +#endif #ifndef GPU_SHADER # undef gpMaterialFlag diff --git a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh index 3b4de704c00..1db98d13c4a 100644 --- a/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh +++ b/source/blender/draw/engines/gpencil/shaders/infos/gpencil_info.hh @@ -20,8 +20,8 @@ GPU_SHADER_INTERFACE_INFO(gpencil_geometry_iface, "gp_interp") GPU_SHADER_CREATE_INFO(gpencil_geometry) .do_static_compilation(true) + .define("GP_LIGHT") .typedef_source("gpencil_defines.h") - .typedef_source("gpencil_shader_shared.h") .sampler(0, ImageType::FLOAT_2D, "gpFillTexture") .sampler(1, ImageType::FLOAT_2D, "gpStrokeTexture") .sampler(2, ImageType::DEPTH_2D, "gpSceneDepthTexture") diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c index e38695c76ab..df5ee6a18c0 100644 --- a/source/blender/draw/engines/overlay/overlay_armature.c +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -2220,7 +2220,7 @@ static void draw_armature_edit(ArmatureDrawContext *ctx) const bool show_text = DRW_state_show_text(); const Object *ob_orig = DEG_get_original_object(ob); - /* FIXME(campbell): We should be able to use the CoW object, + /* FIXME(@campbellbarton): We should be able to use the CoW object, * however the active bone isn't updated. Long term solution is an 'EditArmature' struct. * for now we can draw from the original armature. See: T66773. */ // bArmature *arm = ob->data; diff --git a/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl b/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl index f28a809fdab..606292bbe83 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_antialiasing_frag.glsl @@ -96,7 +96,7 @@ void main() float dist_raw = texelFetch(lineTex, center_texel, 0).b; float dist = decode_line_dist(dist_raw); - /* TODO: Opti: use textureGather. */ + /* TODO: Optimization: use textureGather. */ vec4 neightbor_col0 = texelFetchOffset(colorTex, center_texel, 0, ivec2(1, 0)); vec4 neightbor_col1 = texelFetchOffset(colorTex, center_texel, 0, ivec2(-1, 0)); vec4 neightbor_col2 = texelFetchOffset(colorTex, center_texel, 0, ivec2(0, 1)); diff --git a/source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl b/source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl index 612ce8c6300..ca5a6aff2ca 100644 --- a/source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/overlay_armature_envelope_outline_vert.glsl @@ -130,7 +130,7 @@ void main() gl_Position = p1; /* compute position from 3 vertex because the change in direction - * can happen very quicky and lead to very thin edges. */ + * can happen very quickly and lead to very thin edges. */ vec2 ss0 = proj(p0); vec2 ss1 = proj(p1); vec2 ss2 = proj(p2); diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 88ae5ac707e..026a1f52ac1 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -201,7 +201,7 @@ static void select_cache_populate(void *vedata, Object *ob) if (!e_data.context.is_dirty && sel_data && sel_data->is_drawn) { /* The object indices have already been drawn. Fill depth pass. - * Opti: Most of the time this depth pass is not used. */ + * Optimization: Most of the time this depth pass is not used. */ struct Mesh *me = ob->data; if (e_data.context.select_mode & SCE_SELECT_FACE) { struct GPUBatch *geom_faces = DRW_mesh_batch_cache_get_triangles_with_select_id(me); diff --git a/source/blender/draw/intern/DRW_gpu_wrapper.hh b/source/blender/draw/intern/DRW_gpu_wrapper.hh index 8e61c25be71..5405afd2a90 100644 --- a/source/blender/draw/intern/DRW_gpu_wrapper.hh +++ b/source/blender/draw/intern/DRW_gpu_wrapper.hh @@ -57,6 +57,7 @@ #include "MEM_guardedalloc.h" +#include "draw_manager.h" #include "draw_texture_pool.h" #include "BLI_math_vec_types.hh" @@ -182,7 +183,7 @@ class UniformCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable GPU_uniformbuf_free(ubo_); } - void push_update(void) + void push_update() { GPU_uniformbuf_update(ubo_, this->data_); } @@ -227,12 +228,17 @@ class StorageCommon : public DataBuffer<T, len, false>, NonMovable, NonCopyable GPU_storagebuf_free(ssbo_); } - void push_update(void) + void push_update() { BLI_assert(device_only == false); GPU_storagebuf_update(ssbo_, this->data_); } + void clear_to_zero() + { + GPU_storagebuf_clear_to_zero(ssbo_); + } + operator GPUStorageBuf *() const { return ssbo_; @@ -319,6 +325,7 @@ class StorageArrayBuffer : public detail::StorageCommon<T, len, device_only> { MEM_freeN(this->data_); } + /* Resize to \a new_size elements. */ void resize(int64_t new_size) { BLI_assert(new_size > 0); @@ -595,42 +602,47 @@ class Texture : NonCopyable { /** * Returns true if the texture has been allocated or acquired from the pool. */ - bool is_valid(void) const + bool is_valid() const { return tx_ != nullptr; } - int width(void) const + int width() const { return GPU_texture_width(tx_); } - int height(void) const + int height() const { return GPU_texture_height(tx_); } - bool depth(void) const + int pixel_count() const + { + return GPU_texture_width(tx_) * GPU_texture_height(tx_); + } + + bool depth() const { return GPU_texture_depth(tx_); } - bool is_stencil(void) const + bool is_stencil() const { return GPU_texture_stencil(tx_); } - bool is_integer(void) const + bool is_integer() const { return GPU_texture_integer(tx_); } - bool is_cube(void) const + bool is_cube() const { return GPU_texture_cube(tx_); } - bool is_array(void) const + bool is_array() const { return GPU_texture_array(tx_); } @@ -722,7 +734,7 @@ class Texture : NonCopyable { int3 size = this->size(); if (size != int3(w, h, d) || GPU_texture_format(tx_) != format || GPU_texture_cube(tx_) != cubemap || GPU_texture_array(tx_) != layered) { - GPU_TEXTURE_FREE_SAFE(tx_); + free(); } } if (tx_ == nullptr) { @@ -772,50 +784,45 @@ class Texture : NonCopyable { }; class TextureFromPool : public Texture, NonMovable { - private: - GPUTexture *tx_tmp_saved_ = nullptr; - public: TextureFromPool(const char *name = "gpu::Texture") : Texture(name){}; - /* Always use `release()` after rendering and `sync()` in sync phase. */ - void acquire(int2 extent, eGPUTextureFormat format, void *owner_) + /* Always use `release()` after rendering. */ + void acquire(int2 extent, eGPUTextureFormat format) { BLI_assert(this->tx_ == nullptr); - if (this->tx_ != nullptr) { - return; - } - if (tx_tmp_saved_ != nullptr) { - if (GPU_texture_width(tx_tmp_saved_) != extent.x || - GPU_texture_height(tx_tmp_saved_) != extent.y || - GPU_texture_format(tx_tmp_saved_) != format) { - this->tx_tmp_saved_ = nullptr; - } - else { - this->tx_ = tx_tmp_saved_; - return; - } - } - DrawEngineType *owner = (DrawEngineType *)owner_; - this->tx_ = DRW_texture_pool_query_2d(UNPACK2(extent), format, owner); + + this->tx_ = DRW_texture_pool_texture_acquire( + DST.vmempool->texture_pool, UNPACK2(extent), format); } - void release(void) + void release() { /* Allows multiple release. */ - if (this->tx_ != nullptr) { - tx_tmp_saved_ = this->tx_; - this->tx_ = nullptr; + if (this->tx_ == nullptr) { + return; } + DRW_texture_pool_texture_release(DST.vmempool->texture_pool, this->tx_); + this->tx_ = nullptr; } /** - * Clears any reference. Workaround for pool texture not being able to release on demand. - * Needs to be called at during the sync phase. + * Swap the content of the two textures. + * Also change ownership accordingly if needed. */ - void sync(void) + static void swap(TextureFromPool &a, Texture &b) { - tx_tmp_saved_ = nullptr; + Texture::swap(a, b); + DRW_texture_pool_give_texture_ownership(DST.vmempool->texture_pool, a); + DRW_texture_pool_take_texture_ownership(DST.vmempool->texture_pool, b); + } + static void swap(Texture &a, TextureFromPool &b) + { + swap(b, a); + } + static void swap(TextureFromPool &a, TextureFromPool &b) + { + Texture::swap(a, b); } /** Remove methods that are forbidden with this type of textures. */ @@ -902,45 +909,47 @@ class Framebuffer : NonCopyable { template<typename T, int64_t len> class SwapChain { private: + BLI_STATIC_ASSERT(len > 1, "A swap-chain needs more than 1 unit in length."); std::array<T, len> chain_; - int64_t index_ = 0; public: void swap() { - index_ = (index_ + 1) % len; + for (auto i : IndexRange(len - 1)) { + T::swap(chain_[i], chain_[(i + 1) % len]); + } } T ¤t() { - return chain_[index_]; + return chain_[0]; } T &previous() { /* Avoid modulo operation with negative numbers. */ - return chain_[(index_ + len - 1) % len]; + return chain_[(0 + len - 1) % len]; } T &next() { - return chain_[(index_ + 1) % len]; + return chain_[(0 + 1) % len]; } const T ¤t() const { - return chain_[index_]; + return chain_[0]; } const T &previous() const { /* Avoid modulo operation with negative numbers. */ - return chain_[(index_ + len - 1) % len]; + return chain_[(0 + len - 1) % len]; } const T &next() const { - return chain_[(index_ + 1) % len]; + return chain_[(0 + 1) % len]; } }; diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index c2d26badc4c..a3097251d35 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -454,6 +454,10 @@ void DRW_shgroup_call_compute_indirect(DRWShadingGroup *shgroup, GPUStorageBuf * void DRW_shgroup_call_procedural_points(DRWShadingGroup *sh, Object *ob, uint point_count); void DRW_shgroup_call_procedural_lines(DRWShadingGroup *sh, Object *ob, uint line_count); void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *sh, Object *ob, uint tri_count); +void DRW_shgroup_call_procedural_indirect(DRWShadingGroup *shgroup, + GPUPrimType primitive_type, + Object *ob, + GPUStorageBuf *indirect_buf); /** * \warning Only use with Shaders that have `IN_PLACE_INSTANCES` defined. * TODO: Should be removed. @@ -791,7 +795,7 @@ bool DRW_culling_box_test(const DRWView *view, const BoundBox *bbox); bool DRW_culling_plane_test(const DRWView *view, const float plane[4]); /** * Return True if the given box intersect the current view frustum. - * This function will have to be replaced when world space bb per objects is implemented. + * This function will have to be replaced when world space bounding-box per objects is implemented. */ bool DRW_culling_min_max_test(const DRWView *view, float obmat[4][4], float min[3], float max[3]); diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index f846251c66b..4c0f025e934 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -90,6 +90,7 @@ static struct DRWShapeCache { GPUBatch *drw_procedural_verts; GPUBatch *drw_procedural_lines; GPUBatch *drw_procedural_tris; + GPUBatch *drw_procedural_tri_strips; GPUBatch *drw_cursor; GPUBatch *drw_cursor_only_circle; GPUBatch *drw_fullscreen_quad; @@ -208,6 +209,21 @@ GPUBatch *drw_cache_procedural_triangles_get(void) return SHC.drw_procedural_tris; } +GPUBatch *drw_cache_procedural_triangle_strips_get() +{ + if (!SHC.drw_procedural_tri_strips) { + /* TODO(fclem): get rid of this dummy VBO. */ + GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "dummy", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, 1); + + SHC.drw_procedural_tri_strips = GPU_batch_create_ex( + GPU_PRIM_TRI_STRIP, vbo, NULL, GPU_BATCH_OWNS_VBO); + } + return SHC.drw_procedural_tri_strips; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index af8e58c78f8..0159c9fc86e 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -228,9 +228,9 @@ static void mesh_render_data_polys_sorted_build(MeshRenderData *mr, MeshBufferCa } } else { - const MPoly *mp = &mr->mpoly[0]; - for (int i = 0; i < mr->poly_len; i++, mp++) { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + for (int i = 0; i < mr->poly_len; i++) { + if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[i])) { + const MPoly *mp = &mr->mpoly[i]; const int mat = min_ii(mp->mat_nr, mat_last); tri_first_index[i] = mat_tri_offs[mat]; mat_tri_offs[mat] += mp->totloop - 2; @@ -269,7 +269,7 @@ static void mesh_render_data_mat_tri_len_mesh_range_fn(void *__restrict userdata int *mat_tri_len = static_cast<int *>(tls->userdata_chunk); const MPoly *mp = &mr->mpoly[iter]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + if (!(mr->use_hide && mr->hide_poly && mr->hide_poly[iter])) { int mat = min_ii(mp->mat_nr, mr->mat_len - 1); mat_tri_len[mat] += mp->totloop - 2; } @@ -332,7 +332,7 @@ void mesh_render_data_update_looptris(MeshRenderData *mr, if (mr->extract_type != MR_EXTRACT_BMESH) { /* Mesh */ if ((iter_type & MR_ITER_LOOPTRI) || (data_flag & MR_DATA_LOOPTRI)) { - /* NOTE(campbell): It's possible to skip allocating tessellation, + /* NOTE(@campbellbarton): It's possible to skip allocating tessellation, * the tessellation can be calculated as part of the iterator, see: P2188. * The overall advantage is small (around 1%), so keep this as-is. */ mr->mlooptri = static_cast<MLoopTri *>( @@ -578,6 +578,13 @@ MeshRenderData *mesh_render_data_create(Object *object, mr->v_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->vdata, CD_ORIGINDEX)); mr->e_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->edata, CD_ORIGINDEX)); mr->p_origindex = static_cast<const int *>(CustomData_get_layer(&mr->me->pdata, CD_ORIGINDEX)); + + mr->hide_vert = static_cast<const bool *>( + CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert")); + mr->hide_edge = static_cast<const bool *>( + CustomData_get_layer_named(&me->edata, CD_PROP_BOOL, ".hide_edge")); + mr->hide_poly = static_cast<const bool *>( + CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly")); } else { /* #BMesh */ diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.cc b/source/blender/draw/intern/draw_cache_impl_mesh.cc index d1eb937d711..5de9f1b44c8 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.cc +++ b/source/blender/draw/intern/draw_cache_impl_mesh.cc @@ -293,26 +293,28 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, for (int i = 0; i < gpumat_array_len; i++) { GPUMaterial *gpumat = gpumat_array[i]; - if (gpumat) { - ListBase gpu_attrs = GPU_material_attributes(gpumat); - LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { - const char *name = gpu_attr->name; - eCustomDataType type = static_cast<eCustomDataType>(gpu_attr->type); - int layer = -1; - std::optional<eAttrDomain> domain; - - if (gpu_attr->is_default_color) { - name = default_color_name.c_str(); - } + if (gpumat == nullptr) { + continue; + } + ListBase gpu_attrs = GPU_material_attributes(gpumat); + LISTBASE_FOREACH (GPUMaterialAttribute *, gpu_attr, &gpu_attrs) { + const char *name = gpu_attr->name; + eCustomDataType type = static_cast<eCustomDataType>(gpu_attr->type); + int layer = -1; + std::optional<eAttrDomain> domain; + + if (gpu_attr->is_default_color) { + name = default_color_name.c_str(); + } - if (type == CD_AUTO_FROM_NAME) { - /* We need to deduce what exact layer is used. - * - * We do it based on the specified name. - */ - if (name[0] != '\0') { - layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name); - type = CD_MTFACE; + if (type == CD_AUTO_FROM_NAME) { + /* We need to deduce what exact layer is used. + * + * We do it based on the specified name. + */ + if (name[0] != '\0') { + layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name); + type = CD_MTFACE; #if 0 /* Tangents are always from UV's - this will never happen. */ if (layer == -1) { @@ -320,88 +322,87 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Object *object, type = CD_TANGENT; } #endif - if (layer == -1) { - /* Try to match a generic attribute, we use the first attribute domain with a - * matching name. */ - if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) { - domain = ATTR_DOMAIN_POINT; - } - else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) { - domain = ATTR_DOMAIN_CORNER; - } - else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) { - domain = ATTR_DOMAIN_FACE; - } - else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) { - domain = ATTR_DOMAIN_EDGE; - } - else { - layer = -1; - } + if (layer == -1) { + /* Try to match a generic attribute, we use the first attribute domain with a + * matching name. */ + if (drw_custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; } - - if (layer == -1) { - continue; + else if (drw_custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + domain = ATTR_DOMAIN_CORNER; + } + else if (drw_custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_FACE; + } + else if (drw_custom_data_match_attribute(cd_edata, name, &layer, &type)) { + domain = ATTR_DOMAIN_EDGE; + } + else { + layer = -1; } } - else { - /* Fall back to the UV layer, which matches old behavior. */ - type = CD_MTFACE; + + if (layer == -1) { + continue; } } + else { + /* Fall back to the UV layer, which matches old behavior. */ + type = CD_MTFACE; + } + } - switch (type) { - case CD_MTFACE: { - if (layer == -1) { - layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) : - CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); - } - if (layer != -1) { - cd_used.uv |= (1 << layer); - } - break; + switch (type) { + case CD_MTFACE: { + if (layer == -1) { + layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) : + CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); } - case CD_TANGENT: { - if (layer == -1) { - layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) : - CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); - - /* Only fallback to orco (below) when we have no UV layers, see: T56545 */ - if (layer == -1 && name[0] != '\0') { - layer = CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); - } - } - if (layer != -1) { - cd_used.tan |= (1 << layer); - } - else { - /* no UV layers at all => requesting orco */ - cd_used.tan_orco = 1; - cd_used.orco = 1; + if (layer != -1) { + cd_used.uv |= (1 << layer); + } + break; + } + case CD_TANGENT: { + if (layer == -1) { + layer = (name[0] != '\0') ? CustomData_get_named_layer(cd_ldata, CD_MLOOPUV, name) : + CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); + + /* Only fallback to orco (below) when we have no UV layers, see: T56545 */ + if (layer == -1 && name[0] != '\0') { + layer = CustomData_get_render_layer(cd_ldata, CD_MLOOPUV); } - break; } - - case CD_ORCO: { + if (layer != -1) { + cd_used.tan |= (1 << layer); + } + else { + /* no UV layers at all => requesting orco */ + cd_used.tan_orco = 1; cd_used.orco = 1; - break; } - case CD_PROP_BYTE_COLOR: - case CD_PROP_COLOR: - case CD_PROP_FLOAT3: - case CD_PROP_BOOL: - case CD_PROP_INT8: - case CD_PROP_INT32: - case CD_PROP_FLOAT: - case CD_PROP_FLOAT2: { - if (layer != -1 && domain.has_value()) { - drw_attributes_add_request(attributes, name, type, layer, *domain); - } - break; + break; + } + + case CD_ORCO: { + cd_used.orco = 1; + break; + } + case CD_PROP_BYTE_COLOR: + case CD_PROP_COLOR: + case CD_PROP_FLOAT3: + case CD_PROP_BOOL: + case CD_PROP_INT8: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: { + if (layer != -1 && domain.has_value()) { + drw_attributes_add_request(attributes, name, type, layer, *domain); } - default: - break; + break; } + default: + break; } } } diff --git a/source/blender/draw/intern/draw_cache_impl_pointcloud.c b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc index 55d0eee00e5..d99af0c77e4 100644 --- a/source/blender/draw/intern/draw_cache_impl_pointcloud.c +++ b/source/blender/draw/intern/draw_cache_impl_pointcloud.cc @@ -13,24 +13,23 @@ #include "BLI_math_base.h" #include "BLI_math_vector.h" +#include "BLI_task.hh" #include "BLI_utildefines.h" #include "DNA_object_types.h" #include "DNA_pointcloud_types.h" -#include "BKE_customdata.h" +#include "BKE_attribute.hh" #include "BKE_pointcloud.h" #include "GPU_batch.h" #include "draw_cache_impl.h" /* own include */ -static void pointcloud_batch_cache_clear(PointCloud *pointcloud); - /* ---------------------------------------------------------------------- */ /* PointCloud GPUBatch Cache */ -typedef struct PointCloudBatchCache { +struct PointCloudBatchCache { GPUVertBuf *pos; /* Position and radius. */ GPUVertBuf *geom; /* Instanced geometry for each point in the cloud (small sphere). */ GPUIndexBuf *geom_indices; @@ -43,57 +42,50 @@ typedef struct PointCloudBatchCache { bool is_dirty; int mat_len; -} PointCloudBatchCache; +}; /* GPUBatch cache management. */ -static bool pointcloud_batch_cache_valid(PointCloud *pointcloud) +static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud &pointcloud) +{ + return static_cast<PointCloudBatchCache *>(pointcloud.batch_cache); +} + +static bool pointcloud_batch_cache_valid(PointCloud &pointcloud) { - PointCloudBatchCache *cache = pointcloud->batch_cache; + PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (cache == NULL) { return false; } - if (cache->mat_len != DRW_pointcloud_material_count_get(pointcloud)) { + if (cache->mat_len != DRW_pointcloud_material_count_get(&pointcloud)) { return false; } return cache->is_dirty == false; } -static void pointcloud_batch_cache_init(PointCloud *pointcloud) +static void pointcloud_batch_cache_init(PointCloud &pointcloud) { - PointCloudBatchCache *cache = pointcloud->batch_cache; + PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (!cache) { - cache = pointcloud->batch_cache = MEM_callocN(sizeof(*cache), __func__); + cache = MEM_cnew<PointCloudBatchCache>(__func__); + pointcloud.batch_cache = cache; } else { memset(cache, 0, sizeof(*cache)); } - cache->mat_len = DRW_pointcloud_material_count_get(pointcloud); - cache->surface_per_mat = MEM_callocN(sizeof(GPUBatch *) * cache->mat_len, - "pointcloud suface_per_mat"); + cache->mat_len = DRW_pointcloud_material_count_get(&pointcloud); + cache->surface_per_mat = static_cast<GPUBatch **>( + MEM_callocN(sizeof(GPUBatch *) * cache->mat_len, __func__)); cache->is_dirty = false; } -void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud) -{ - if (!pointcloud_batch_cache_valid(pointcloud)) { - pointcloud_batch_cache_clear(pointcloud); - pointcloud_batch_cache_init(pointcloud); - } -} - -static PointCloudBatchCache *pointcloud_batch_cache_get(PointCloud *pointcloud) -{ - return pointcloud->batch_cache; -} - void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode) { - PointCloudBatchCache *cache = pointcloud->batch_cache; + PointCloudBatchCache *cache = pointcloud_batch_cache_get(*pointcloud); if (cache == NULL) { return; } @@ -106,9 +98,9 @@ void DRW_pointcloud_batch_cache_dirty_tag(PointCloud *pointcloud, int mode) } } -static void pointcloud_batch_cache_clear(PointCloud *pointcloud) +static void pointcloud_batch_cache_clear(PointCloud &pointcloud) { - PointCloudBatchCache *cache = pointcloud->batch_cache; + PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (!cache) { return; } @@ -127,54 +119,65 @@ static void pointcloud_batch_cache_clear(PointCloud *pointcloud) MEM_SAFE_FREE(cache->surface_per_mat); } +void DRW_pointcloud_batch_cache_validate(PointCloud *pointcloud) +{ + if (!pointcloud_batch_cache_valid(*pointcloud)) { + pointcloud_batch_cache_clear(*pointcloud); + pointcloud_batch_cache_init(*pointcloud); + } +} + void DRW_pointcloud_batch_cache_free(PointCloud *pointcloud) { - pointcloud_batch_cache_clear(pointcloud); + pointcloud_batch_cache_clear(*pointcloud); MEM_SAFE_FREE(pointcloud->batch_cache); } -static void pointcloud_batch_cache_ensure_pos(Object *ob, PointCloudBatchCache *cache) +static void pointcloud_batch_cache_ensure_pos(const PointCloud &pointcloud, + PointCloudBatchCache &cache) { - if (cache->pos != NULL) { + using namespace blender; + if (cache.pos != NULL) { return; } - PointCloud *pointcloud = ob->data; - const float(*positions)[3] = (float(*)[3])CustomData_get_layer_named( - &pointcloud->pdata, CD_PROP_FLOAT3, "position"); - const float *radii = (float *)CustomData_get_layer_named( - &pointcloud->pdata, CD_PROP_FLOAT, "radius"); - const bool has_radius = radii != NULL; - - static GPUVertFormat format = {0}; - static GPUVertFormat format_no_radius = {0}; - static uint pos; - if (format.attr_len == 0) { - /* initialize vertex format */ - /* From the opengl wiki: - * Note that size does not have to exactly match the size used by the vertex shader. If the - * vertex shader has fewer components than the attribute provides, then the extras are ignored. - * If the vertex shader has more components than the array provides, the extras are given - * values from the vector (0, 0, 0, 1) for the missing XYZW components. - */ - pos = GPU_vertformat_attr_add(&format_no_radius, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - } - - cache->pos = GPU_vertbuf_create_with_format(has_radius ? &format : &format_no_radius); - GPU_vertbuf_data_alloc(cache->pos, pointcloud->totpoint); - - if (has_radius) { - float(*vbo_data)[4] = (float(*)[4])GPU_vertbuf_get_data(cache->pos); - for (int i = 0; i < pointcloud->totpoint; i++) { - copy_v3_v3(vbo_data[i], positions[i]); - /* TODO(fclem): remove multiplication here. - * Here only for keeping the size correct for now. */ - vbo_data[i][3] = radii[i] * 100.0f; + const bke::AttributeAccessor attributes = bke::pointcloud_attributes(pointcloud); + const VArraySpan<float3> positions = attributes.lookup<float3>("position", ATTR_DOMAIN_POINT); + const VArray<float> radii = attributes.lookup<float>("radius", ATTR_DOMAIN_POINT); + /* From the opengl wiki: + * Note that size does not have to exactly match the size used by the vertex shader. If the + * vertex shader has fewer components than the attribute provides, then the extras are ignored. + * If the vertex shader has more components than the array provides, the extras are given + * values from the vector (0, 0, 0, 1) for the missing XYZW components. */ + if (radii) { + static GPUVertFormat format = {0}; + if (format.attr_len == 0) { + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); } + cache.pos = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache.pos, positions.size()); + const VArraySpan<float> radii_span(radii); + MutableSpan<float4> vbo_data{static_cast<float4 *>(GPU_vertbuf_get_data(cache.pos)), + pointcloud.totpoint}; + threading::parallel_for(vbo_data.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + vbo_data[i].x = positions[i].x; + vbo_data[i].y = positions[i].y; + vbo_data[i].z = positions[i].z; + /* TODO(fclem): remove multiplication. Here only for keeping the size correct for now. */ + vbo_data[i].w = radii_span[i] * 100.0f; + } + }); } else { - GPU_vertbuf_attr_fill(cache->pos, pos, positions); + static GPUVertFormat format = {0}; + static uint pos; + if (format.attr_len == 0) { + pos = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + } + cache.pos = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache.pos, positions.size()); + GPU_vertbuf_attr_fill(cache.pos, pos, positions.data()); } } @@ -193,24 +196,23 @@ static const uint half_octahedron_tris[4][3] = { {0, 4, 1}, }; -static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBatchCache *cache) +static void pointcloud_batch_cache_ensure_geom(PointCloudBatchCache &cache) { - if (cache->geom != NULL) { + if (cache.geom != NULL) { return; } static GPUVertFormat format = {0}; static uint pos; if (format.attr_len == 0) { - /* initialize vertex format */ pos = GPU_vertformat_attr_add(&format, "pos_inst", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); GPU_vertformat_alias_add(&format, "nor"); } - cache->geom = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(cache->geom, ARRAY_SIZE(half_octahedron_normals)); + cache.geom = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache.geom, ARRAY_SIZE(half_octahedron_normals)); - GPU_vertbuf_attr_fill(cache->geom, pos, half_octahedron_normals); + GPU_vertbuf_attr_fill(cache.geom, pos, half_octahedron_normals); GPUIndexBufBuilder builder; GPU_indexbuf_init(&builder, @@ -222,16 +224,16 @@ static void pointcloud_batch_cache_ensure_geom(Object *UNUSED(ob), PointCloudBat GPU_indexbuf_add_tri_verts(&builder, UNPACK3(half_octahedron_tris[i])); } - cache->geom_indices = GPU_indexbuf_build(&builder); + cache.geom_indices = GPU_indexbuf_build(&builder); } GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob) { - PointCloud *pointcloud = ob->data; + PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data); PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (cache->dots == NULL) { - pointcloud_batch_cache_ensure_pos(ob, cache); + pointcloud_batch_cache_ensure_pos(pointcloud, *cache); cache->dots = GPU_batch_create(GPU_PRIM_POINTS, cache->pos, NULL); } @@ -240,12 +242,12 @@ GPUBatch *DRW_pointcloud_batch_cache_get_dots(Object *ob) GPUBatch *DRW_pointcloud_batch_cache_get_surface(Object *ob) { - PointCloud *pointcloud = ob->data; + PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data); PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); if (cache->surface == NULL) { - pointcloud_batch_cache_ensure_pos(ob, cache); - pointcloud_batch_cache_ensure_geom(ob, cache); + pointcloud_batch_cache_ensure_pos(pointcloud, *cache); + pointcloud_batch_cache_ensure_geom(*cache); cache->surface = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices); GPU_batch_instbuf_add_ex(cache->surface, cache->pos, false); @@ -258,14 +260,14 @@ GPUBatch **DRW_cache_pointcloud_surface_shaded_get(Object *ob, struct GPUMaterial **UNUSED(gpumat_array), uint gpumat_array_len) { - PointCloud *pointcloud = ob->data; + PointCloud &pointcloud = *static_cast<PointCloud *>(ob->data); PointCloudBatchCache *cache = pointcloud_batch_cache_get(pointcloud); BLI_assert(cache->mat_len == gpumat_array_len); UNUSED_VARS(gpumat_array_len); if (cache->surface_per_mat[0] == NULL) { - pointcloud_batch_cache_ensure_pos(ob, cache); - pointcloud_batch_cache_ensure_geom(ob, cache); + pointcloud_batch_cache_ensure_pos(pointcloud, *cache); + pointcloud_batch_cache_ensure_geom(*cache); cache->surface_per_mat[0] = GPU_batch_create(GPU_PRIM_TRIS, cache->geom, cache->geom_indices); GPU_batch_instbuf_add_ex(cache->surface_per_mat[0], cache->pos, false); diff --git a/source/blender/draw/intern/draw_cache_impl_subdivision.cc b/source/blender/draw/intern/draw_cache_impl_subdivision.cc index 2f8a2540776..075e08eb49f 100644 --- a/source/blender/draw/intern/draw_cache_impl_subdivision.cc +++ b/source/blender/draw/intern/draw_cache_impl_subdivision.cc @@ -668,7 +668,9 @@ static void draw_subdiv_cache_extra_coarse_face_data_bm(BMesh *bm, } } -static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t *flags_data) +static void draw_subdiv_cache_extra_coarse_face_data_mesh(const MeshRenderData *mr, + Mesh *mesh, + uint32_t *flags_data) { for (int i = 0; i < mesh->totpoly; i++) { uint32_t flag = 0; @@ -678,7 +680,7 @@ static void draw_subdiv_cache_extra_coarse_face_data_mesh(Mesh *mesh, uint32_t * if ((mesh->mpoly[i].flag & ME_FACE_SEL) != 0) { flag |= SUBDIV_COARSE_FACE_FLAG_SELECT; } - if ((mesh->mpoly[i].flag & ME_HIDE) != 0) { + if (mr->hide_poly && mr->hide_poly[i]) { flag |= SUBDIV_COARSE_FACE_FLAG_HIDDEN; } flags_data[i] = (uint)(mesh->mpoly[i].loopstart) | (flag << SUBDIV_COARSE_FACE_FLAG_OFFSET); @@ -691,7 +693,7 @@ static void draw_subdiv_cache_extra_coarse_face_data_mapped(Mesh *mesh, uint32_t *flags_data) { if (bm == nullptr) { - draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data); + draw_subdiv_cache_extra_coarse_face_data_mesh(mr, mesh, flags_data); return; } @@ -726,7 +728,7 @@ static void draw_subdiv_cache_update_extra_coarse_face_data(DRWSubdivCache *cach draw_subdiv_cache_extra_coarse_face_data_mapped(mesh, cache->bm, mr, flags_data); } else { - draw_subdiv_cache_extra_coarse_face_data_mesh(mesh, flags_data); + draw_subdiv_cache_extra_coarse_face_data_mesh(mr, mesh, flags_data); } /* Make sure updated data is re-uploaded. */ diff --git a/source/blender/draw/intern/draw_debug.c b/source/blender/draw/intern/draw_debug.c deleted file mode 100644 index b568119627e..00000000000 --- a/source/blender/draw/intern/draw_debug.c +++ /dev/null @@ -1,196 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later - * Copyright 2018 Blender Foundation. */ - -/** \file - * \ingroup draw - * - * \brief Simple API to draw debug shapes in the viewport. - */ - -#include "MEM_guardedalloc.h" - -#include "DNA_object_types.h" - -#include "BKE_object.h" - -#include "BLI_link_utils.h" - -#include "GPU_immediate.h" -#include "GPU_matrix.h" - -#include "draw_debug.h" -#include "draw_manager.h" - -/* --------- Register --------- */ - -/* Matrix applied to all points before drawing. Could be a stack if needed. */ -static float g_modelmat[4][4]; - -void DRW_debug_modelmat_reset(void) -{ - unit_m4(g_modelmat); -} - -void DRW_debug_modelmat(const float modelmat[4][4]) -{ - copy_m4_m4(g_modelmat, modelmat); -} - -void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]) -{ - DRWDebugLine *line = MEM_mallocN(sizeof(DRWDebugLine), "DRWDebugLine"); - mul_v3_m4v3(line->pos[0], g_modelmat, v1); - mul_v3_m4v3(line->pos[1], g_modelmat, v2); - copy_v4_v4(line->color, color); - BLI_LINKS_PREPEND(DST.debug.lines, line); -} - -void DRW_debug_polygon_v3(const float (*v)[3], const int vert_len, const float color[4]) -{ - BLI_assert(vert_len > 1); - - for (int i = 0; i < vert_len; i++) { - DRW_debug_line_v3v3(v[i], v[(i + 1) % vert_len], color); - } -} - -void DRW_debug_m4(const float m[4][4]) -{ - float v0[3] = {0.0f, 0.0f, 0.0f}; - float v1[3] = {1.0f, 0.0f, 0.0f}; - float v2[3] = {0.0f, 1.0f, 0.0f}; - float v3[3] = {0.0f, 0.0f, 1.0f}; - - mul_m4_v3(m, v0); - mul_m4_v3(m, v1); - mul_m4_v3(m, v2); - mul_m4_v3(m, v3); - - DRW_debug_line_v3v3(v0, v1, (float[4]){1.0f, 0.0f, 0.0f, 1.0f}); - DRW_debug_line_v3v3(v0, v2, (float[4]){0.0f, 1.0f, 0.0f, 1.0f}); - DRW_debug_line_v3v3(v0, v3, (float[4]){0.0f, 0.0f, 1.0f, 1.0f}); -} - -void DRW_debug_bbox(const BoundBox *bbox, const float color[4]) -{ - DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[1], color); - DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[2], color); - DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[3], color); - DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[0], color); - - DRW_debug_line_v3v3(bbox->vec[4], bbox->vec[5], color); - DRW_debug_line_v3v3(bbox->vec[5], bbox->vec[6], color); - DRW_debug_line_v3v3(bbox->vec[6], bbox->vec[7], color); - DRW_debug_line_v3v3(bbox->vec[7], bbox->vec[4], color); - - DRW_debug_line_v3v3(bbox->vec[0], bbox->vec[4], color); - DRW_debug_line_v3v3(bbox->vec[1], bbox->vec[5], color); - DRW_debug_line_v3v3(bbox->vec[2], bbox->vec[6], color); - DRW_debug_line_v3v3(bbox->vec[3], bbox->vec[7], color); -} - -void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], const bool invert) -{ - BoundBox bb; - const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; - float project_matrix[4][4]; - if (invert) { - invert_m4_m4(project_matrix, m); - } - else { - copy_m4_m4(project_matrix, m); - } - - BKE_boundbox_init_from_minmax(&bb, min, max); - for (int i = 0; i < 8; i++) { - mul_project_m4_v3(project_matrix, bb.vec[i]); - } - DRW_debug_bbox(&bb, color); -} - -void DRW_debug_sphere(const float center[3], const float radius, const float color[4]) -{ - float size_mat[4][4]; - DRWDebugSphere *sphere = MEM_mallocN(sizeof(DRWDebugSphere), "DRWDebugSphere"); - /* Bake all transform into a Matrix4 */ - scale_m4_fl(size_mat, radius); - copy_m4_m4(sphere->mat, g_modelmat); - translate_m4(sphere->mat, center[0], center[1], center[2]); - mul_m4_m4m4(sphere->mat, sphere->mat, size_mat); - - copy_v4_v4(sphere->color, color); - BLI_LINKS_PREPEND(DST.debug.spheres, sphere); -} - -/* --------- Render --------- */ - -static void drw_debug_draw_lines(void) -{ - int count = BLI_linklist_count((LinkNode *)DST.debug.lines); - - if (count == 0) { - return; - } - - GPUVertFormat *vert_format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(vert_format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR); - - immBegin(GPU_PRIM_LINES, count * 2); - - while (DST.debug.lines) { - void *next = DST.debug.lines->next; - - immAttr4fv(col, DST.debug.lines->color); - immVertex3fv(pos, DST.debug.lines->pos[0]); - - immAttr4fv(col, DST.debug.lines->color); - immVertex3fv(pos, DST.debug.lines->pos[1]); - - MEM_freeN(DST.debug.lines); - DST.debug.lines = next; - } - immEnd(); - - immUnbindProgram(); -} - -static void drw_debug_draw_spheres(void) -{ - int count = BLI_linklist_count((LinkNode *)DST.debug.spheres); - - if (count == 0) { - return; - } - - float persmat[4][4]; - DRW_view_persmat_get(NULL, persmat, false); - - GPUBatch *empty_sphere = DRW_cache_empty_sphere_get(); - GPU_batch_program_set_builtin(empty_sphere, GPU_SHADER_3D_UNIFORM_COLOR); - while (DST.debug.spheres) { - void *next = DST.debug.spheres->next; - float MVP[4][4]; - - mul_m4_m4m4(MVP, persmat, DST.debug.spheres->mat); - GPU_batch_uniform_mat4(empty_sphere, "ModelViewProjectionMatrix", MVP); - GPU_batch_uniform_4fv(empty_sphere, "color", DST.debug.spheres->color); - GPU_batch_draw(empty_sphere); - - MEM_freeN(DST.debug.spheres); - DST.debug.spheres = next; - } -} - -void drw_debug_draw(void) -{ - drw_debug_draw_lines(); - drw_debug_draw_spheres(); -} - -void drw_debug_init(void) -{ - DRW_debug_modelmat_reset(); -} diff --git a/source/blender/draw/intern/draw_debug.cc b/source/blender/draw/intern/draw_debug.cc new file mode 100644 index 00000000000..aaf18014143 --- /dev/null +++ b/source/blender/draw/intern/draw_debug.cc @@ -0,0 +1,721 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2018 Blender Foundation. */ + +/** \file + * \ingroup draw + * + * \brief Simple API to draw debug shapes in the viewport. + */ + +#include "BKE_object.h" +#include "BLI_link_utils.h" +#include "GPU_batch.h" +#include "GPU_capabilities.h" +#include "GPU_debug.h" + +#include "draw_debug.h" +#include "draw_debug.hh" +#include "draw_manager.h" +#include "draw_shader.h" +#include "draw_shader_shared.h" + +#include <iomanip> + +namespace blender::draw { + +/* -------------------------------------------------------------------- */ +/** \name Init and state + * \{ */ + +DebugDraw::DebugDraw() +{ + constexpr int circle_resolution = 16; + for (auto axis : IndexRange(3)) { + for (auto edge : IndexRange(circle_resolution)) { + for (auto vert : IndexRange(2)) { + const float angle = (2 * M_PI) * (edge + vert) / float(circle_resolution); + float point[3] = {cosf(angle), sinf(angle), 0.0f}; + sphere_verts_.append( + float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3])); + } + } + } + + constexpr int point_resolution = 4; + for (auto axis : IndexRange(3)) { + for (auto edge : IndexRange(point_resolution)) { + for (auto vert : IndexRange(2)) { + const float angle = (2 * M_PI) * (edge + vert) / float(point_resolution); + float point[3] = {cosf(angle), sinf(angle), 0.0f}; + point_verts_.append( + float3(point[(0 + axis) % 3], point[(1 + axis) % 3], point[(2 + axis) % 3])); + } + } + } +}; + +void DebugDraw::init() +{ + cpu_print_buf_.command.v_count = 0; + cpu_print_buf_.command.v_first = 0; + cpu_print_buf_.command.i_count = 1; + cpu_print_buf_.command.i_first = 0; + + cpu_draw_buf_.command.v_count = 0; + cpu_draw_buf_.command.v_first = 0; + cpu_draw_buf_.command.i_count = 1; + cpu_draw_buf_.command.i_first = 0; + + gpu_print_buf_.command.v_count = 0; + gpu_print_buf_.command.v_first = 0; + gpu_print_buf_.command.i_count = 1; + gpu_print_buf_.command.i_first = 0; + gpu_print_buf_used = false; + + gpu_draw_buf_.command.v_count = 0; + gpu_draw_buf_.command.v_first = 0; + gpu_draw_buf_.command.i_count = 1; + gpu_draw_buf_.command.i_first = 0; + gpu_draw_buf_used = false; + + modelmat_reset(); +} + +void DebugDraw::modelmat_reset() +{ + model_mat_ = float4x4::identity(); +} + +void DebugDraw::modelmat_set(const float modelmat[4][4]) +{ + model_mat_ = modelmat; +} + +GPUStorageBuf *DebugDraw::gpu_draw_buf_get() +{ + BLI_assert(GPU_shader_storage_buffer_objects_support()); + if (!gpu_draw_buf_used) { + gpu_draw_buf_used = true; + gpu_draw_buf_.push_update(); + } + return gpu_draw_buf_; +} + +GPUStorageBuf *DebugDraw::gpu_print_buf_get() +{ + BLI_assert(GPU_shader_storage_buffer_objects_support()); + if (!gpu_print_buf_used) { + gpu_print_buf_used = true; + gpu_print_buf_.push_update(); + } + return gpu_print_buf_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw functions + * \{ */ + +void DebugDraw::draw_line(float3 v1, float3 v2, float4 color) +{ + draw_line(v1, v2, color_pack(color)); +} + +void DebugDraw::draw_polygon(Span<float3> poly_verts, float4 color) +{ + BLI_assert(!poly_verts.is_empty()); + + uint col = color_pack(color); + float3 v0 = model_mat_ * poly_verts.last(); + for (auto vert : poly_verts) { + float3 v1 = model_mat_ * vert; + draw_line(v0, v1, col); + v0 = v1; + } +} + +void DebugDraw::draw_matrix(const float4x4 m4) +{ + float3 v0 = float3(0.0f, 0.0f, 0.0f); + float3 v1 = float3(1.0f, 0.0f, 0.0f); + float3 v2 = float3(0.0f, 1.0f, 0.0f); + float3 v3 = float3(0.0f, 0.0f, 1.0f); + + mul_project_m4_v3(m4.ptr(), v0); + mul_project_m4_v3(m4.ptr(), v1); + mul_project_m4_v3(m4.ptr(), v2); + mul_project_m4_v3(m4.ptr(), v3); + + draw_line(v0, v1, float4(1.0f, 0.0f, 0.0f, 1.0f)); + draw_line(v0, v2, float4(0.0f, 1.0f, 0.0f, 1.0f)); + draw_line(v0, v3, float4(0.0f, 0.0f, 1.0f, 1.0f)); +} + +void DebugDraw::draw_bbox(const BoundBox &bbox, const float4 color) +{ + uint col = color_pack(color); + draw_line(bbox.vec[0], bbox.vec[1], col); + draw_line(bbox.vec[1], bbox.vec[2], col); + draw_line(bbox.vec[2], bbox.vec[3], col); + draw_line(bbox.vec[3], bbox.vec[0], col); + + draw_line(bbox.vec[4], bbox.vec[5], col); + draw_line(bbox.vec[5], bbox.vec[6], col); + draw_line(bbox.vec[6], bbox.vec[7], col); + draw_line(bbox.vec[7], bbox.vec[4], col); + + draw_line(bbox.vec[0], bbox.vec[4], col); + draw_line(bbox.vec[1], bbox.vec[5], col); + draw_line(bbox.vec[2], bbox.vec[6], col); + draw_line(bbox.vec[3], bbox.vec[7], col); +} + +void DebugDraw::draw_matrix_as_bbox(float4x4 mat, const float4 color) +{ + BoundBox bb; + const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; + BKE_boundbox_init_from_minmax(&bb, min, max); + for (auto i : IndexRange(8)) { + mul_project_m4_v3(mat.ptr(), bb.vec[i]); + } + draw_bbox(bb, color); +} + +void DebugDraw::draw_sphere(const float3 center, float radius, const float4 color) +{ + uint col = color_pack(color); + for (auto i : IndexRange(sphere_verts_.size() / 2)) { + float3 v0 = sphere_verts_[i * 2] * radius + center; + float3 v1 = sphere_verts_[i * 2 + 1] * radius + center; + draw_line(v0, v1, col); + } +} + +void DebugDraw::draw_point(const float3 center, float radius, const float4 color) +{ + uint col = color_pack(color); + for (auto i : IndexRange(point_verts_.size() / 2)) { + float3 v0 = point_verts_[i * 2] * radius + center; + float3 v1 = point_verts_[i * 2 + 1] * radius + center; + draw_line(v0, v1, col); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Print functions + * \{ */ + +template<> void DebugDraw::print_value<uint>(const uint &value) +{ + print_value_uint(value, false, false, true); +} +template<> void DebugDraw::print_value<int>(const int &value) +{ + print_value_uint(uint(abs(value)), false, (value < 0), false); +} +template<> void DebugDraw::print_value<bool>(const bool &value) +{ + print_string(value ? "true " : "false"); +} +template<> void DebugDraw::print_value<float>(const float &val) +{ + std::stringstream ss; + ss << std::setw(12) << std::to_string(val); + print_string(ss.str()); +} +template<> void DebugDraw::print_value<double>(const double &val) +{ + print_value(float(val)); +} + +template<> void DebugDraw::print_value_hex<uint>(const uint &value) +{ + print_value_uint(value, true, false, false); +} +template<> void DebugDraw::print_value_hex<int>(const int &value) +{ + print_value_uint(uint(value), true, false, false); +} +template<> void DebugDraw::print_value_hex<float>(const float &value) +{ + print_value_uint(*reinterpret_cast<const uint *>(&value), true, false, false); +} +template<> void DebugDraw::print_value_hex<double>(const double &val) +{ + print_value_hex(float(val)); +} + +template<> void DebugDraw::print_value_binary<uint>(const uint &value) +{ + print_value_binary(value); +} +template<> void DebugDraw::print_value_binary<int>(const int &value) +{ + print_value_binary(uint(value)); +} +template<> void DebugDraw::print_value_binary<float>(const float &value) +{ + print_value_binary(*reinterpret_cast<const uint *>(&value)); +} +template<> void DebugDraw::print_value_binary<double>(const double &val) +{ + print_value_binary(float(val)); +} + +template<> void DebugDraw::print_value<float2>(const float2 &value) +{ + print_no_endl("float2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<float3>(const float3 &value) +{ + print_no_endl("float3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<float4>(const float4 &value) +{ + print_no_endl("float4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +template<> void DebugDraw::print_value<int2>(const int2 &value) +{ + print_no_endl("int2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<int3>(const int3 &value) +{ + print_no_endl("int3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<int4>(const int4 &value) +{ + print_no_endl("int4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +template<> void DebugDraw::print_value<uint2>(const uint2 &value) +{ + print_no_endl("uint2(", value[0], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<uint3>(const uint3 &value) +{ + print_no_endl("uint3(", value[0], ", ", value[1], ", ", value[1], ")"); +} +template<> void DebugDraw::print_value<uint4>(const uint4 &value) +{ + print_no_endl("uint4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Internals + * + * IMPORTANT: All of these are copied from the shader libs (common_debug_draw_lib.glsl & + * common_debug_print_lib.glsl). They need to be kept in sync to write the same data. + * \{ */ + +void DebugDraw::draw_line(float3 v1, float3 v2, uint color) +{ + DebugDrawBuf &buf = cpu_draw_buf_; + uint index = buf.command.v_count; + if (index + 2 < DRW_DEBUG_DRAW_VERT_MAX) { + buf.verts[index + 0] = vert_pack(model_mat_ * v1, color); + buf.verts[index + 1] = vert_pack(model_mat_ * v2, color); + buf.command.v_count += 2; + } +} + +/* Keep in sync with drw_debug_color_pack(). */ +uint DebugDraw::color_pack(float4 color) +{ + color = math::clamp(color, 0.0f, 1.0f); + uint result = 0; + result |= uint(color.x * 255.0f) << 0u; + result |= uint(color.y * 255.0f) << 8u; + result |= uint(color.z * 255.0f) << 16u; + result |= uint(color.w * 255.0f) << 24u; + return result; +} + +DRWDebugVert DebugDraw::vert_pack(float3 pos, uint color) +{ + DRWDebugVert vert; + vert.pos0 = *reinterpret_cast<uint32_t *>(&pos.x); + vert.pos1 = *reinterpret_cast<uint32_t *>(&pos.y); + vert.pos2 = *reinterpret_cast<uint32_t *>(&pos.z); + vert.color = color; + return vert; +} + +void DebugDraw::print_newline() +{ + print_col_ = 0u; + print_row_ = ++cpu_print_buf_.command.i_first; +} + +void DebugDraw::print_string_start(uint len) +{ + /* Break before word. */ + if (print_col_ + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + print_newline(); + } +} + +/* Copied from gpu_shader_dependency. */ +void DebugDraw::print_string(std::string str) +{ + size_t len_before_pad = str.length(); + /* Pad string to uint size to avoid out of bound reads. */ + while (str.length() % 4 != 0) { + str += " "; + } + + print_string_start(len_before_pad); + for (size_t i = 0; i < len_before_pad; i += 4) { + union { + uint8_t chars[4]; + uint32_t word; + }; + + chars[0] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0); + chars[1] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1); + chars[2] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2); + chars[3] = *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3); + + if (i + 4 > len_before_pad) { + chars[len_before_pad - i] = '\0'; + } + print_char4(word); + } +} + +/* Keep in sync with shader. */ +void DebugDraw::print_char4(uint data) +{ + /* Convert into char stream. */ + for (; data != 0u; data >>= 8u) { + uint char1 = data & 0xFFu; + /* Check for null terminator. */ + if (char1 == 0x00) { + break; + } + /* NOTE: Do not skip the header manually like in GPU. */ + uint cursor = cpu_print_buf_.command.v_count++; + if (cursor < DRW_DEBUG_PRINT_MAX) { + /* For future usage. (i.e: Color) */ + uint flags = 0u; + uint col = print_col_++; + uint print_header = (flags << 24u) | (print_row_ << 16u) | (col << 8u); + cpu_print_buf_.char_array[cursor] = print_header | char1; + /* Break word. */ + if (print_col_ > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + print_newline(); + } + } + } +} + +void DebugDraw::print_append_char(uint char1, uint &char4) +{ + char4 = (char4 << 8u) | char1; +} + +void DebugDraw::print_append_digit(uint digit, uint &char4) +{ + const uint char_A = 0x41u; + const uint char_0 = 0x30u; + bool is_hexadecimal = digit > 9u; + char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit)); +} + +void DebugDraw::print_append_space(uint &char4) +{ + char4 = (char4 << 8u) | 0x20u; +} + +void DebugDraw::print_value_binary(uint value) +{ + print_string("0b"); + print_string_start(10u * 4u); + uint digits[10] = {0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u}; + uint digit = 0u; + for (uint i = 0u; i < 32u; i++) { + print_append_digit(((value >> i) & 1u), digits[digit / 4u]); + digit++; + if ((i % 4u) == 3u) { + print_append_space(digits[digit / 4u]); + digit++; + } + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 9; j >= 0; j--) { + print_char4(digits[j]); + } +} + +void DebugDraw::print_value_uint(uint value, + const bool hex, + bool is_negative, + const bool is_unsigned) +{ + print_string_start(3u * 4u); + const uint blank_value = hex ? 0x30303030u : 0x20202020u; + const uint prefix = hex ? 0x78302020u : 0x20202020u; + uint digits[3] = {blank_value, blank_value, prefix}; + const uint base = hex ? 16u : 10u; + uint digit = 0u; + /* Add `u` suffix. */ + if (is_unsigned) { + print_append_char('u', digits[digit / 4u]); + digit++; + } + /* Number's digits. */ + for (; value != 0u || digit == uint(is_unsigned); value /= base) { + print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Add negative sign. */ + if (is_negative) { + print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + print_char4(digits[j]); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Display + * \{ */ + +void DebugDraw::display_lines() +{ + if (cpu_draw_buf_.command.v_count == 0 && gpu_draw_buf_used == false) { + return; + } + GPU_debug_group_begin("Lines"); + cpu_draw_buf_.push_update(); + + float4x4 persmat; + const DRWView *view = DRW_view_get_active(); + DRW_view_persmat_get(view, persmat.ptr(), false); + + drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS); + + GPUBatch *batch = drw_cache_procedural_lines_get(); + GPUShader *shader = DRW_shader_debug_draw_display_get(); + GPU_batch_set_shader(batch, shader); + int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS); + GPU_shader_uniform_mat4(shader, "persmat", persmat.ptr()); + + if (gpu_draw_buf_used) { + GPU_debug_group_begin("GPU"); + GPU_storagebuf_bind(gpu_draw_buf_, slot); + GPU_batch_draw_indirect(batch, gpu_draw_buf_); + GPU_storagebuf_unbind(gpu_draw_buf_); + GPU_debug_group_end(); + } + + GPU_debug_group_begin("CPU"); + GPU_storagebuf_bind(cpu_draw_buf_, slot); + GPU_batch_draw_indirect(batch, cpu_draw_buf_); + GPU_storagebuf_unbind(cpu_draw_buf_); + GPU_debug_group_end(); + + GPU_debug_group_end(); +} + +void DebugDraw::display_prints() +{ + if (cpu_print_buf_.command.v_count == 0 && gpu_print_buf_used == false) { + return; + } + GPU_debug_group_begin("Prints"); + cpu_print_buf_.push_update(); + + drw_state_set(DRW_STATE_WRITE_COLOR | DRW_STATE_PROGRAM_POINT_SIZE); + + GPUBatch *batch = drw_cache_procedural_points_get(); + GPUShader *shader = DRW_shader_debug_print_display_get(); + GPU_batch_set_shader(batch, shader); + int slot = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); + + if (gpu_print_buf_used) { + GPU_debug_group_begin("GPU"); + GPU_storagebuf_bind(gpu_print_buf_, slot); + GPU_batch_draw_indirect(batch, gpu_print_buf_); + GPU_storagebuf_unbind(gpu_print_buf_); + GPU_debug_group_end(); + } + + GPU_debug_group_begin("CPU"); + GPU_storagebuf_bind(cpu_print_buf_, slot); + GPU_batch_draw_indirect(batch, cpu_print_buf_); + GPU_storagebuf_unbind(cpu_print_buf_); + GPU_debug_group_end(); + + GPU_debug_group_end(); +} + +void DebugDraw::display_to_view() +{ + GPU_debug_group_begin("DebugDraw"); + + display_lines(); + /* Print 3D shapes before text to avoid overlaps. */ + display_prints(); + /* Init again so we don't draw the same thing twice. */ + init(); + + GPU_debug_group_end(); +} + +} // namespace blender::draw + +blender::draw::DebugDraw *DRW_debug_get() +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return nullptr; + } + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API private + * \{ */ + +void drw_debug_draw() +{ +#ifdef DEBUG + if (!GPU_shader_storage_buffer_objects_support() || DST.debug == nullptr) { + return; + } + /* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */ + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->display_to_view(); +#endif +} + +/** + * NOTE: Init is once per draw manager cycle. + */ +void drw_debug_init() +{ + /* Module should not be used in release builds. */ + /* TODO(@fclem): Hide the functions declarations without using `ifdefs` everywhere. */ +#ifdef DEBUG + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + /* TODO(@fclem): Convenience for now. Will have to move to #DRWManager. */ + if (DST.debug == nullptr) { + DST.debug = reinterpret_cast<DRWDebugModule *>(new blender::draw::DebugDraw()); + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->init(); +#endif +} + +void drw_debug_module_free(DRWDebugModule *module) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + if (module != nullptr) { + delete reinterpret_cast<blender::draw::DebugDraw *>(module); + } +} + +GPUStorageBuf *drw_debug_gpu_draw_buf_get() +{ + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_draw_buf_get(); +} + +GPUStorageBuf *drw_debug_gpu_print_buf_get() +{ + return reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->gpu_print_buf_get(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name C-API public + * \{ */ + +void DRW_debug_modelmat_reset() +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_reset(); +} + +void DRW_debug_modelmat(const float modelmat[4][4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->modelmat_set(modelmat); +} + +void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_line(v1, v2, color); +} + +void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_polygon( + blender::Span<float3>((float3 *)v, vert_len), color); +} + +void DRW_debug_m4(const float m[4][4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix(m); +} + +void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + blender::float4x4 m4 = m; + if (invert) { + m4 = m4.inverted(); + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_matrix_as_bbox(m4, color); +} + +void DRW_debug_bbox(const BoundBox *bbox, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_bbox(*bbox, color); +} + +void DRW_debug_sphere(const float center[3], float radius, const float color[4]) +{ + if (!GPU_shader_storage_buffer_objects_support()) { + return; + } + reinterpret_cast<blender::draw::DebugDraw *>(DST.debug)->draw_sphere(center, radius, color); +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_debug.h b/source/blender/draw/intern/draw_debug.h index 333d734edb9..9a56a12242e 100644 --- a/source/blender/draw/intern/draw_debug.h +++ b/source/blender/draw/intern/draw_debug.h @@ -3,21 +3,38 @@ /** \file * \ingroup draw + * + * \brief Simple API to draw debug shapes in the viewport. + * IMPORTANT: This is the legacy API for C. Use draw_debug.hh instead in new C++ code. */ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct DRWDebugModule DRWDebugModule; + struct BoundBox; void DRW_debug_modelmat_reset(void); void DRW_debug_modelmat(const float modelmat[4][4]); +/** + * IMPORTANT: For now there is a limit of DRW_DEBUG_DRAW_VERT_MAX that can be drawn + * using all the draw functions. + */ void DRW_debug_line_v3v3(const float v1[3], const float v2[3], const float color[4]); void DRW_debug_polygon_v3(const float (*v)[3], int vert_len, const float color[4]); /** * \note g_modelmat is still applied on top. */ void DRW_debug_m4(const float m[4][4]); -void DRW_debug_m4_as_bbox(const float m[4][4], const float color[4], bool invert); +void DRW_debug_m4_as_bbox(const float m[4][4], bool invert, const float color[4]); void DRW_debug_bbox(const BoundBox *bbox, const float color[4]); void DRW_debug_sphere(const float center[3], float radius, const float color[4]); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_debug.hh b/source/blender/draw/intern/draw_debug.hh new file mode 100644 index 00000000000..c83936bf1af --- /dev/null +++ b/source/blender/draw/intern/draw_debug.hh @@ -0,0 +1,198 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2022 Blender Foundation. */ + +/** \file + * \ingroup draw + * + * \brief Simple API to draw debug shapes and log in the viewport. + * + * Both CPU and GPU implementation are supported and symmetrical (meaning GPU shader can use it + * too, see common_debug_print/draw_lib.glsl). + * + * NOTE: CPU logging will overlap GPU logging on screen as it is drawn after. + */ + +#pragma once + +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" +#include "DNA_object_types.h" +#include "DRW_gpu_wrapper.hh" + +namespace blender::draw { + +/* Shortcuts to avoid boilerplate code and match shader API. */ +#define drw_debug_line(...) DRW_debug_get()->draw_line(__VA_ARGS__) +#define drw_debug_polygon(...) DRW_debug_get()->draw_polygon(__VA_ARGS__) +#define drw_debug_bbox(...) DRW_debug_get()->draw_bbox(__VA_ARGS__) +#define drw_debug_sphere(...) DRW_debug_get()->draw_sphere(__VA_ARGS__) +#define drw_debug_point(...) DRW_debug_get()->draw_point(__VA_ARGS__) +#define drw_debug_matrix(...) DRW_debug_get()->draw_matrix(__VA_ARGS__) +#define drw_debug_matrix_as_bbox(...) DRW_debug_get()->draw_matrix_as_bbox(__VA_ARGS__) +#define drw_print(...) DRW_debug_get()->print(__VA_ARGS__) +#define drw_print_hex(...) DRW_debug_get()->print_hex(__VA_ARGS__) +#define drw_print_binary(...) DRW_debug_get()->print_binary(__VA_ARGS__) +#define drw_print_no_endl(...) DRW_debug_get()->print_no_endl(__VA_ARGS__) + +/* Will log variable along with its name, like the shader version of print(). */ +#define drw_print_id(v_) DRW_debug_get()->print(#v_, "= ", v_) +#define drw_print_id_no_endl(v_) DRW_debug_get()->print_no_endl(#v_, "= ", v_) + +class DebugDraw { + private: + using DebugDrawBuf = StorageBuffer<DRWDebugDrawBuffer>; + using DebugPrintBuf = StorageBuffer<DRWDebugPrintBuffer>; + + /** Data buffers containing all verts or chars to draw. */ + DebugDrawBuf cpu_draw_buf_ = {"DebugDrawBuf-CPU"}; + DebugDrawBuf gpu_draw_buf_ = {"DebugDrawBuf-GPU"}; + DebugPrintBuf cpu_print_buf_ = {"DebugPrintBuf-CPU"}; + DebugPrintBuf gpu_print_buf_ = {"DebugPrintBuf-GPU"}; + /** True if the gpu buffer have been requested and may contain data to draw. */ + bool gpu_print_buf_used = false; + bool gpu_draw_buf_used = false; + /** Matrix applied to all points before drawing. Could be a stack if needed. */ + float4x4 model_mat_; + /** Precomputed shapes verts. */ + Vector<float3> sphere_verts_; + Vector<float3> point_verts_; + /** Cursor position for print functionality. */ + uint print_col_ = 0; + uint print_row_ = 0; + + public: + DebugDraw(); + ~DebugDraw(){}; + + /** + * Resets all buffers and reset model matrix state. + * Not to be called by user. + */ + void init(); + + /** + * Resets model matrix state to identity. + */ + void modelmat_reset(); + /** + * Sets model matrix transform to apply to any vertex passed to drawing functions. + */ + void modelmat_set(const float modelmat[4][4]); + + /** + * Drawing functions that will draw wire-frames with the given color. + */ + void draw_line(float3 v1, float3 v2, float4 color = {1, 0, 0, 1}); + void draw_polygon(Span<float3> poly_verts, float4 color = {1, 0, 0, 1}); + void draw_bbox(const BoundBox &bbox, const float4 color = {1, 0, 0, 1}); + void draw_sphere(const float3 center, float radius, const float4 color = {1, 0, 0, 1}); + void draw_point(const float3 center, float radius = 0.01f, const float4 color = {1, 0, 0, 1}); + /** + * Draw a matrix transformation as 3 colored axes. + */ + void draw_matrix(const float4x4 m4); + /** + * Draw a matrix as a 2 units length bounding box, centered on origin. + */ + void draw_matrix_as_bbox(float4x4 mat, const float4 color = {1, 0, 0, 1}); + + /** + * Will draw all debug shapes and text cached up until now to the current view / frame-buffer. + * Draw buffers will be emptied and ready for new debug data. + */ + void display_to_view(); + + /** + * Log variable or strings inside the viewport. + * Using a unique non string argument will print the variable name with it. + * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`. + */ + template<typename... Ts> void print(StringRefNull str, Ts... args) + { + print_no_endl(str, args...); + print_newline(); + } + template<typename T> void print(const T &value) + { + print_value(value); + print_newline(); + } + template<typename T> void print_hex(const T &value) + { + print_value_hex(value); + print_newline(); + } + template<typename T> void print_binary(const T &value) + { + print_value_binary(value); + print_newline(); + } + + /** + * Same as `print()` but does not finish the line. + */ + void print_no_endl(std::string arg) + { + print_string(arg); + } + void print_no_endl(StringRef arg) + { + print_string(arg); + } + void print_no_endl(StringRefNull arg) + { + print_string(arg); + } + void print_no_endl(char const *arg) + { + print_string(StringRefNull(arg)); + } + template<typename T> void print_no_endl(T arg) + { + print_value(arg); + } + template<typename T, typename... Ts> void print_no_endl(T arg, Ts... args) + { + print_no_endl(arg); + print_no_endl(args...); + } + + /** + * Not to be called by user. Should become private. + */ + GPUStorageBuf *gpu_draw_buf_get(); + GPUStorageBuf *gpu_print_buf_get(); + + private: + uint color_pack(float4 color); + DRWDebugVert vert_pack(float3 pos, uint color); + + void draw_line(float3 v1, float3 v2, uint color); + + void print_newline(); + void print_string_start(uint len); + void print_string(std::string str); + void print_char4(uint data); + void print_append_char(uint char1, uint &char4); + void print_append_digit(uint digit, uint &char4); + void print_append_space(uint &char4); + void print_value_binary(uint value); + void print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned); + + template<typename T> void print_value(const T &value); + template<typename T> void print_value_hex(const T &value); + template<typename T> void print_value_binary(const T &value); + + void display_lines(); + void display_prints(); +}; + +} // namespace blender::draw + +/** + * Ease of use function to get the debug module. + * TODO(fclem): Should be removed once DRWManager is no longer global. + * IMPORTANT: Can return nullptr if storage buffer is not supported. + */ +blender::draw::DebugDraw *DRW_debug_get(); diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index b2422504825..4693e5f8e20 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -43,6 +43,7 @@ #include "DNA_camera_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_userdef_types.h" #include "DNA_world_types.h" #include "ED_gpencil.h" @@ -84,6 +85,7 @@ #include "draw_cache_impl.h" #include "engines/basic/basic_engine.h" +#include "engines/compositor/compositor_engine.h" #include "engines/eevee/eevee_engine.h" #include "engines/eevee_next/eevee_engine.h" #include "engines/external/external_engine.h" @@ -1214,6 +1216,31 @@ static void drw_engines_enable_editors(void) } } +static bool is_compositor_enabled(void) +{ + if (!U.experimental.use_realtime_compositor) { + return false; + } + + if (!(DST.draw_ctx.v3d->shading.flag & V3D_SHADING_COMPOSITOR)) { + return false; + } + + if (!(DST.draw_ctx.v3d->shading.type >= OB_MATERIAL)) { + return false; + } + + if (!DST.draw_ctx.scene->use_nodes) { + return false; + } + + if (!DST.draw_ctx.scene->nodetree) { + return false; + } + + return true; +} + static void drw_engines_enable(ViewLayer *UNUSED(view_layer), RenderEngineType *engine_type, bool gpencil_engine_needed) @@ -1226,6 +1253,11 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer), if (gpencil_engine_needed && ((drawtype >= OB_SOLID) || !use_xray)) { use_drw_engine(&draw_engine_gpencil_type); } + + if (is_compositor_enabled()) { + use_drw_engine(&draw_engine_compositor_type); + } + drw_engines_enable_overlays(); #ifdef WITH_DRAW_DEBUG @@ -1597,7 +1629,6 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, GPUViewport *viewport, const bContext *evil_C) { - Scene *scene = DEG_get_evaluated_scene(depsgraph); ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); RegionView3D *rv3d = region->regiondata; @@ -2948,6 +2979,7 @@ void DRW_engines_register(void) DRW_engine_register(&draw_engine_overlay_type); DRW_engine_register(&draw_engine_select_type); DRW_engine_register(&draw_engine_basic_type); + DRW_engine_register(&draw_engine_compositor_type); #ifdef WITH_DRAW_DEBUG DRW_engine_register(&draw_engine_debug_select_type); #endif @@ -3028,6 +3060,9 @@ void DRW_engines_free(void) DRW_stats_free(); DRW_globals_free(); + drw_debug_module_free(DST.debug); + DST.debug = NULL; + DRW_UBO_FREE_SAFE(G_draw.block_ubo); DRW_UBO_FREE_SAFE(G_draw.view_ubo); DRW_TEXTURE_FREE_SAFE(G_draw.ramp); diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index 419b13edf1f..a29f2fa7507 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -188,6 +188,7 @@ typedef enum { DRW_CMD_DRAW_INSTANCE = 2, DRW_CMD_DRAW_INSTANCE_RANGE = 3, DRW_CMD_DRAW_PROCEDURAL = 4, + DRW_CMD_DRAW_INDIRECT = 5, /* Compute Commands. */ DRW_CMD_COMPUTE = 8, @@ -203,7 +204,7 @@ typedef enum { /* Needs to fit in 4bits */ } eDRWCommandType; -#define DRW_MAX_DRAW_CMD_TYPE DRW_CMD_DRAW_PROCEDURAL +#define DRW_MAX_DRAW_CMD_TYPE DRW_CMD_DRAW_INDIRECT typedef struct DRWCommandDraw { GPUBatch *batch; @@ -232,6 +233,12 @@ typedef struct DRWCommandDrawInstanceRange { uint inst_count; } DRWCommandDrawInstanceRange; +typedef struct DRWCommandDrawIndirect { + GPUBatch *batch; + DRWResourceHandle handle; + GPUStorageBuf *indirect_buf; +} DRWCommandDrawIndirect; + typedef struct DRWCommandCompute { int groups_x_len; int groups_y_len; @@ -286,6 +293,7 @@ typedef union DRWCommand { DRWCommandDrawInstance instance; DRWCommandDrawInstanceRange instance_range; DRWCommandDrawProcedural procedural; + DRWCommandDrawIndirect draw_indirect; DRWCommandCompute compute; DRWCommandComputeRef compute_ref; DRWCommandComputeIndirect compute_indirect; @@ -493,20 +501,6 @@ typedef struct DRWCommandSmallChunk { BLI_STATIC_ASSERT_ALIGN(DRWCommandChunk, 16); #endif -/* ------------- DRAW DEBUG ------------ */ - -typedef struct DRWDebugLine { - struct DRWDebugLine *next; /* linked list */ - float pos[2][3]; - float color[4]; -} DRWDebugLine; - -typedef struct DRWDebugSphere { - struct DRWDebugSphere *next; /* linked list */ - float mat[4][4]; - float color[4]; -} DRWDebugSphere; - /* ------------- Memory Pools ------------ */ /* Contains memory pools information */ @@ -648,11 +642,7 @@ typedef struct DRWManager { GPUDrawList *draw_list; - struct { - /* TODO(@fclem): optimize: use chunks. */ - DRWDebugLine *lines; - DRWDebugSphere *spheres; - } debug; + DRWDebugModule *debug; } DRWManager; extern DRWManager DST; /* TODO: get rid of this and allow multi-threaded rendering. */ @@ -667,6 +657,9 @@ void drw_state_set(DRWState state); void drw_debug_draw(void); void drw_debug_init(void); +void drw_debug_module_free(DRWDebugModule *module); +GPUStorageBuf *drw_debug_gpu_draw_buf_get(void); +GPUStorageBuf *drw_debug_gpu_print_buf_get(void); eDRWCommandType command_type_get(const uint64_t *command_type_bits, int index); @@ -685,6 +678,7 @@ void drw_resource_buffer_finish(DRWData *vmempool); GPUBatch *drw_cache_procedural_points_get(void); GPUBatch *drw_cache_procedural_lines_get(void); GPUBatch *drw_cache_procedural_triangles_get(void); +GPUBatch *drw_cache_procedural_triangle_strips_get(void); void drw_uniform_attrs_pool_update(struct GHash *table, struct GPUUniformAttrList *key, diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index 188d9114cd7..1a5d91444fe 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -17,9 +17,14 @@ #include "BKE_pbvh.h" #include "BKE_volume.h" +/* For debug cursor position. */ +#include "WM_api.h" +#include "wm_window.h" + #include "DNA_curve_types.h" #include "DNA_mesh_types.h" #include "DNA_meta_types.h" +#include "DNA_screen_types.h" #include "BLI_alloca.h" #include "BLI_hash.h" @@ -39,6 +44,16 @@ #include "intern/gpu_codegen.h" +/** + * IMPORTANT: + * In order to be able to write to the same print buffer sequentially, we add a barrier to allow + * multiple shader calls writing to the same buffer. + * However, this adds explicit synchronization events which might change the rest of the + * application behavior and hide some bugs. If you know you are using shader debug print in only + * one shader pass, you can comment this out to remove the aforementioned barrier. + */ +#define DISABLE_DEBUG_SHADER_PRINT_BARRIER + /* -------------------------------------------------------------------- */ /** \name Uniform Buffer Object (DRW_uniformbuffer) * \{ */ @@ -878,6 +893,17 @@ static void drw_command_draw_procedural(DRWShadingGroup *shgroup, cmd->vert_count = vert_count; } +static void drw_command_draw_indirect(DRWShadingGroup *shgroup, + GPUBatch *batch, + DRWResourceHandle handle, + GPUStorageBuf *indirect_buf) +{ + DRWCommandDrawIndirect *cmd = drw_command_create(shgroup, DRW_CMD_DRAW_INDIRECT); + cmd->batch = batch; + cmd->handle = handle; + cmd->indirect_buf = indirect_buf; +} + static void drw_command_set_select_id(DRWShadingGroup *shgroup, GPUVertBuf *buf, uint select_id) { /* Only one can be valid. */ @@ -1005,6 +1031,7 @@ void DRW_shgroup_call_compute_indirect(DRWShadingGroup *shgroup, GPUStorageBuf * drw_command_compute_indirect(shgroup, indirect_buf); } + void DRW_shgroup_barrier(DRWShadingGroup *shgroup, eGPUBarrier type) { BLI_assert(GPU_compute_shader_support()); @@ -1044,6 +1071,38 @@ void DRW_shgroup_call_procedural_triangles(DRWShadingGroup *shgroup, Object *ob, drw_shgroup_call_procedural_add_ex(shgroup, geom, ob, tri_count * 3); } +void DRW_shgroup_call_procedural_indirect(DRWShadingGroup *shgroup, + GPUPrimType primitive_type, + Object *ob, + GPUStorageBuf *indirect_buf) +{ + struct GPUBatch *geom = NULL; + switch (primitive_type) { + case GPU_PRIM_POINTS: + geom = drw_cache_procedural_points_get(); + break; + case GPU_PRIM_LINES: + geom = drw_cache_procedural_lines_get(); + break; + case GPU_PRIM_TRIS: + geom = drw_cache_procedural_triangles_get(); + break; + case GPU_PRIM_TRI_STRIP: + geom = drw_cache_procedural_triangle_strips_get(); + break; + default: + BLI_assert_msg(0, + "Unsupported primitive type in DRW_shgroup_call_procedural_indirect. Add new " + "one as needed."); + break; + } + if (G.f & G_FLAG_PICKSEL) { + drw_command_set_select_id(shgroup, NULL, DST.select_id); + } + DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); + drw_command_draw_indirect(shgroup, geom, handle, indirect_buf); +} + void DRW_shgroup_call_instances(DRWShadingGroup *shgroup, Object *ob, struct GPUBatch *geom, @@ -1466,6 +1525,27 @@ static void drw_shgroup_init(DRWShadingGroup *shgroup, GPUShader *shader) shgroup, view_ubo_location, DRW_UNIFORM_BLOCK, G_draw.view_ubo, 0, 0, 1); } +#ifdef DEBUG + int debug_print_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_PRINT); + if (debug_print_location != -1) { + GPUStorageBuf *buf = drw_debug_gpu_print_buf_get(); + drw_shgroup_uniform_create_ex( + shgroup, debug_print_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1); +# ifndef DISABLE_DEBUG_SHADER_PRINT_BARRIER + /* Add a barrier to allow multiple shader writing to the same buffer. */ + DRW_shgroup_barrier(shgroup, GPU_BARRIER_SHADER_STORAGE); +# endif + } + + int debug_draw_location = GPU_shader_get_builtin_ssbo(shader, GPU_STORAGE_BUFFER_DEBUG_VERTS); + if (debug_draw_location != -1) { + GPUStorageBuf *buf = drw_debug_gpu_draw_buf_get(); + drw_shgroup_uniform_create_ex( + shgroup, debug_draw_location, DRW_UNIFORM_STORAGE_BLOCK, buf, 0, 0, 1); + /* NOTE(fclem): No barrier as ordering is not important. */ + } +#endif + /* Not supported. */ BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV) == -1); BLI_assert(GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW) == -1); @@ -1942,6 +2022,13 @@ DRWView *DRW_view_create(const float viewmat[4][4], copy_v4_fl4(view->storage.viewcamtexcofac, 1.0f, 1.0f, 0.0f, 0.0f); + if (DST.draw_ctx.evil_C && DST.draw_ctx.region) { + int region_origin[2] = {DST.draw_ctx.region->winrct.xmin, DST.draw_ctx.region->winrct.ymin}; + struct wmWindow *win = CTX_wm_window(DST.draw_ctx.evil_C); + wm_cursor_position_get(win, &view->storage.mouse_pixel[0], &view->storage.mouse_pixel[1]); + sub_v2_v2v2_int(view->storage.mouse_pixel, view->storage.mouse_pixel, region_origin); + } + DRW_view_update(view, viewmat, winmat, culling_viewmat, culling_winmat); return view; @@ -2041,6 +2128,14 @@ void DRW_view_update(DRWView *view, draw_frustum_bound_sphere_calc( &view->frustum_corners, viewinv, winmat, wininv, &view->frustum_bsphere); + /* TODO(fclem): Deduplicate. */ + for (int i = 0; i < 8; i++) { + copy_v3_v3(view->storage.frustum_corners[i], view->frustum_corners.vec[i]); + } + for (int i = 0; i < 6; i++) { + copy_v4_v4(view->storage.frustum_planes[i], view->frustum_planes[i]); + } + #ifdef DRW_DEBUG_CULLING if (G.debug_value != 0) { DRW_debug_sphere( diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index e7e0e0ce41f..4dda0ceb2ef 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -318,6 +318,7 @@ void DRW_state_reset(void) DRW_state_reset_ex(DRW_STATE_DEFAULT); GPU_texture_unbind_all(); + GPU_texture_image_unbind_all(); GPU_uniformbuf_unbind_all(); GPU_storagebuf_unbind_all(); @@ -874,6 +875,25 @@ static void draw_call_single_do(DRWShadingGroup *shgroup, state->baseinst_loc); } +/* Not to be mistaken with draw_indirect_call which does batch many drawcalls together. This one + * only execute an indirect drawcall with user indirect buffer. */ +static void draw_call_indirect(DRWShadingGroup *shgroup, + DRWCommandsState *state, + GPUBatch *batch, + DRWResourceHandle handle, + GPUStorageBuf *indirect_buf) +{ + draw_call_batching_flush(shgroup, state); + draw_call_resource_bind(state, &handle); + + if (G.f & G_FLAG_PICKSEL) { + GPU_select_load_id(state->select_id); + } + + GPU_batch_set_shader(batch, shgroup->shader); + GPU_batch_draw_indirect(batch, indirect_buf); +} + static void draw_call_batching_start(DRWCommandsState *state) { state->neg_scale = false; @@ -970,6 +990,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) /* Unbinding can be costly. Skip in normal condition. */ if (G.debug & G_DEBUG_GPU) { GPU_texture_unbind_all(); + GPU_texture_image_unbind_all(); GPU_uniformbuf_unbind_all(); GPU_storagebuf_unbind_all(); } @@ -996,12 +1017,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) while ((cmd = draw_command_iter_step(&iter, &cmd_type))) { switch (cmd_type) { + case DRW_CMD_DRAW_PROCEDURAL: case DRW_CMD_DRWSTATE: case DRW_CMD_STENCIL: draw_call_batching_flush(shgroup, &state); break; case DRW_CMD_DRAW: - case DRW_CMD_DRAW_PROCEDURAL: + case DRW_CMD_DRAW_INDIRECT: case DRW_CMD_DRAW_INSTANCE: if (draw_call_is_culled(&cmd->instance.handle, DST.view_active)) { continue; @@ -1055,6 +1077,13 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) 1, true); break; + case DRW_CMD_DRAW_INDIRECT: + draw_call_indirect(shgroup, + &state, + cmd->draw_indirect.batch, + cmd->draw_indirect.handle, + cmd->draw_indirect.indirect_buf); + break; case DRW_CMD_DRAW_INSTANCE: draw_call_single_do(shgroup, &state, diff --git a/source/blender/draw/intern/draw_shader.cc b/source/blender/draw/intern/draw_shader.cc index 001ceb0ae8d..ecb30d54b64 100644 --- a/source/blender/draw/intern/draw_shader.cc +++ b/source/blender/draw/intern/draw_shader.cc @@ -24,6 +24,8 @@ extern "C" char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; static struct { struct GPUShader *hair_refine_sh[PART_REFINE_MAX_SHADER]; + struct GPUShader *debug_print_display_sh; + struct GPUShader *debug_draw_display_sh; } e_data = {{nullptr}}; /* -------------------------------------------------------------------- */ @@ -109,6 +111,22 @@ GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineSh return e_data.hair_refine_sh[type]; } +GPUShader *DRW_shader_debug_print_display_get() +{ + if (e_data.debug_print_display_sh == nullptr) { + e_data.debug_print_display_sh = GPU_shader_create_from_info_name("draw_debug_print_display"); + } + return e_data.debug_print_display_sh; +} + +GPUShader *DRW_shader_debug_draw_display_get() +{ + if (e_data.debug_draw_display_sh == nullptr) { + e_data.debug_draw_display_sh = GPU_shader_create_from_info_name("draw_debug_draw_display"); + } + return e_data.debug_draw_display_sh; +} + /** \} */ void DRW_shaders_free() @@ -116,4 +134,6 @@ void DRW_shaders_free() for (int i = 0; i < PART_REFINE_MAX_SHADER; i++) { DRW_SHADER_FREE_SAFE(e_data.hair_refine_sh[i]); } + DRW_SHADER_FREE_SAFE(e_data.debug_print_display_sh); + DRW_SHADER_FREE_SAFE(e_data.debug_draw_display_sh); } diff --git a/source/blender/draw/intern/draw_shader.h b/source/blender/draw/intern/draw_shader.h index 63d755cc334..dabb4b3327f 100644 --- a/source/blender/draw/intern/draw_shader.h +++ b/source/blender/draw/intern/draw_shader.h @@ -30,6 +30,9 @@ struct GPUShader *DRW_shader_hair_refine_get(ParticleRefineShader refinement, struct GPUShader *DRW_shader_curves_refine_get(CurvesEvalShader type, eParticleRefineShaderType sh_type); +struct GPUShader *DRW_shader_debug_print_display_get(void); +struct GPUShader *DRW_shader_debug_draw_display_get(void); + void DRW_shaders_free(void); #ifdef __cplusplus diff --git a/source/blender/draw/intern/draw_shader_shared.h b/source/blender/draw/intern/draw_shader_shared.h index e8944442607..90a6475c42b 100644 --- a/source/blender/draw/intern/draw_shader_shared.h +++ b/source/blender/draw/intern/draw_shader_shared.h @@ -1,6 +1,8 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef GPU_SHADER +# pragma once + # include "GPU_shader.h" # include "GPU_shader_shared_utils.h" @@ -9,6 +11,12 @@ typedef struct ObjectMatrices ObjectMatrices; typedef struct ObjectInfos ObjectInfos; typedef struct VolumeInfos VolumeInfos; typedef struct CurvesInfos CurvesInfos; +typedef struct DrawCommand DrawCommand; +typedef struct DrawCommandIndexed DrawCommandIndexed; +typedef struct DispatchCommand DispatchCommand; +typedef struct DRWDebugPrintBuffer DRWDebugPrintBuffer; +typedef struct DRWDebugVert DRWDebugVert; +typedef struct DRWDebugDrawBuffer DRWDebugDrawBuffer; #endif #define DRW_SHADER_SHARED_H @@ -43,6 +51,12 @@ struct ViewInfos { /** NOTE: vec3 arrays are padded to vec4. */ float4 frustum_corners[8]; float4 frustum_planes[6]; + + /** For debugging purpose */ + /* Mouse pixel. */ + int2 mouse_pixel; + + int2 _pad0; }; BLI_STATIC_ASSERT_ALIGN(ViewInfos, 16) @@ -96,3 +110,89 @@ BLI_STATIC_ASSERT_ALIGN(CurvesInfos, 16) #define OrcoTexCoFactors (drw_infos[resource_id].drw_OrcoTexCoFactors) #define ObjectInfo (drw_infos[resource_id].drw_Infos) #define ObjectColor (drw_infos[resource_id].drw_ObjectColor) + +/* Indirect commands structures. */ + +struct DrawCommand { + uint v_count; + uint i_count; + uint v_first; + uint i_first; +}; +BLI_STATIC_ASSERT_ALIGN(DrawCommand, 16) + +struct DrawCommandIndexed { + uint v_count; + uint i_count; + uint v_first; + uint base_index; + uint i_first; + uint _pad0; + uint _pad1; + uint _pad2; +}; +BLI_STATIC_ASSERT_ALIGN(DrawCommandIndexed, 16) + +struct DispatchCommand { + uint num_groups_x; + uint num_groups_y; + uint num_groups_z; + uint _pad0; +}; +BLI_STATIC_ASSERT_ALIGN(DispatchCommand, 16) + +/* -------------------------------------------------------------------- */ +/** \name Debug print + * \{ */ + +/* Take the header (DrawCommand) into account. */ +#define DRW_DEBUG_PRINT_MAX (8 * 1024) - 4 +/* NOTE: Cannot be more than 255 (because of column encoding). */ +#define DRW_DEBUG_PRINT_WORD_WRAP_COLUMN 120u + +/* The debug print buffer is laid-out as the following struct. + * But we use plain array in shader code instead because of driver issues. */ +struct DRWDebugPrintBuffer { + DrawCommand command; + /** Each character is encoded as 3 `uchar` with char_index, row and column position. */ + uint char_array[DRW_DEBUG_PRINT_MAX]; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16) + +/* Use number of char as vertex count. Equivalent to `DRWDebugPrintBuffer.command.v_count`. */ +#define drw_debug_print_cursor drw_debug_print_buf[0] +/* Reuse first instance as row index as we don't use instancing. Equivalent to + * `DRWDebugPrintBuffer.command.i_first`. */ +#define drw_debug_print_row_shared drw_debug_print_buf[3] + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug draw shapes + * \{ */ + +struct DRWDebugVert { + /* This is a weird layout, but needed to be able to use DRWDebugVert as + * a DrawCommand and avoid alignment issues. See drw_debug_verts_buf[] definition. */ + uint pos0; + uint pos1; + uint pos2; + uint color; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugVert, 16) + +/* Take the header (DrawCommand) into account. */ +#define DRW_DEBUG_DRAW_VERT_MAX (64 * 1024) - 1 + +/* The debug draw buffer is laid-out as the following struct. + * But we use plain array in shader code instead because of driver issues. */ +struct DRWDebugDrawBuffer { + DrawCommand command; + DRWDebugVert verts[DRW_DEBUG_DRAW_VERT_MAX]; +}; +BLI_STATIC_ASSERT_ALIGN(DRWDebugPrintBuffer, 16) + +/* Equivalent to `DRWDebugDrawBuffer.command.v_count`. */ +#define drw_debug_draw_v_count drw_debug_verts_buf[0].pos0 + +/** \} */ diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc index b36cb5c809e..017ecec7be2 100644 --- a/source/blender/draw/intern/draw_texture_pool.cc +++ b/source/blender/draw/intern/draw_texture_pool.cc @@ -160,6 +160,19 @@ void DRW_texture_pool_texture_release(DRWTexturePool *pool, GPUTexture *tmp_tex) pool->tmp_tex_released.append(tmp_tex); } +void DRW_texture_pool_take_texture_ownership(DRWTexturePool *pool, GPUTexture *tex) +{ + pool->tmp_tex_acquired.remove_first_occurrence_and_reorder(tex); +} + +void DRW_texture_pool_give_texture_ownership(DRWTexturePool *pool, GPUTexture *tex) +{ + BLI_assert(pool->tmp_tex_acquired.first_index_of_try(tex) == -1 && + pool->tmp_tex_released.first_index_of_try(tex) == -1 && + pool->tmp_tex_pruned.first_index_of_try(tex) == -1); + pool->tmp_tex_acquired.append(tex); +} + void DRW_texture_pool_reset(DRWTexturePool *pool) { pool->last_user_id = -1; diff --git a/source/blender/draw/intern/draw_texture_pool.h b/source/blender/draw/intern/draw_texture_pool.h index 1c30ea88552..9fbbf630833 100644 --- a/source/blender/draw/intern/draw_texture_pool.h +++ b/source/blender/draw/intern/draw_texture_pool.h @@ -26,6 +26,7 @@ void DRW_texture_pool_free(DRWTexturePool *pool); /** * Try to find a texture corresponding to params into the texture pool. * If no texture was found, create one and add it to the pool. + * DEPRECATED: Use DRW_texture_pool_texture_acquire instead and do it just before rendering. */ GPUTexture *DRW_texture_pool_query( DRWTexturePool *pool, int width, int height, eGPUTextureFormat format, void *user); @@ -40,6 +41,22 @@ GPUTexture *DRW_texture_pool_texture_acquire(DRWTexturePool *pool, * Releases a previously acquired texture. */ void DRW_texture_pool_texture_release(DRWTexturePool *pool, GPUTexture *tmp_tex); + +/** + * This effectively remove a texture from the texture pool, giving full ownership to the caller. + * The given texture needs to be been acquired through DRW_texture_pool_texture_acquire(). + * IMPORTANT: This removes the need for a DRW_texture_pool_texture_release() call on this texture. + */ +void DRW_texture_pool_take_texture_ownership(DRWTexturePool *pool, GPUTexture *tex); +/** + * This Inserts a texture into the texture pool, giving full ownership to the texture pool. + * The texture needs not to be in the pool already. + * The texture may be reused in a latter call to DRW_texture_pool_texture_acquire(); + * IMPORTANT: DRW_texture_pool_texture_release() still needs to be called on this texture + * after usage. + */ +void DRW_texture_pool_give_texture_ownership(DRWTexturePool *pool, GPUTexture *tex); + /** * Resets the user bits for each texture in the pool and delete unused ones. */ diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh index a4773736f0c..57abf088c16 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.hh +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.hh @@ -83,6 +83,9 @@ struct MeshRenderData { MLoopTri *mlooptri; const float (*vert_normals)[3]; const float (*poly_normals)[3]; + const bool *hide_vert; + const bool *hide_edge; + const bool *hide_poly; float (*loop_normals)[3]; int *lverts, *ledges; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc index 2ff093d0bd8..9f82cc56941 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_edituv.cc @@ -61,7 +61,7 @@ static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); const MPoly *mp = &mr->mpoly[mlt->poly]; edituv_tri_add(data, - (mp->flag & ME_HIDE) != 0, + mr->hide_poly && mr->hide_poly[mlt->poly], (mp->flag & ME_FACE_SEL) != 0, mlt->tri[0], mlt->tri[1], @@ -117,7 +117,7 @@ static void extract_edituv_tris_iter_subdiv_bm(const DRWSubdivCache *UNUSED(subd } static void extract_edituv_tris_iter_subdiv_mesh(const DRWSubdivCache *UNUSED(subdiv_cache), - const MeshRenderData *UNUSED(mr), + const MeshRenderData *mr, void *_data, uint subdiv_quad_index, const MPoly *coarse_quad) @@ -125,19 +125,13 @@ static void extract_edituv_tris_iter_subdiv_mesh(const DRWSubdivCache *UNUSED(su MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); const uint loop_idx = subdiv_quad_index * 4; - edituv_tri_add(data, - (coarse_quad->flag & ME_HIDE) != 0, - (coarse_quad->flag & ME_FACE_SEL) != 0, - loop_idx, - loop_idx + 1, - loop_idx + 2); + const bool hidden = mr->hide_poly && mr->hide_poly[coarse_quad - mr->mpoly]; - edituv_tri_add(data, - (coarse_quad->flag & ME_HIDE) != 0, - (coarse_quad->flag & ME_FACE_SEL) != 0, - loop_idx, - loop_idx + 2, - loop_idx + 3); + edituv_tri_add( + data, hidden, (coarse_quad->flag & ME_FACE_SEL) != 0, loop_idx, loop_idx + 1, loop_idx + 2); + + edituv_tri_add( + data, hidden, (coarse_quad->flag & ME_FACE_SEL) != 0, loop_idx, loop_idx + 2, loop_idx + 3); } static void extract_edituv_tris_finish_subdiv(const struct DRWSubdivCache *UNUSED(subdiv_cache), @@ -218,6 +212,8 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { @@ -227,11 +223,8 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, const int ml_index_next = (ml_index == ml_index_last) ? mp->loopstart : (ml_index + 1); const bool real_edge = (mr->e_origindex == nullptr || mr->e_origindex[ml->e] != ORIGINDEX_NONE); - edituv_edge_add(data, - (mp->flag & ME_HIDE) != 0 || !real_edge, - (mp->flag & ME_FACE_SEL) != 0, - ml_index, - ml_index_next); + edituv_edge_add( + data, hidden || !real_edge, (mp->flag & ME_FACE_SEL) != 0, ml_index, ml_index_next); } } @@ -288,6 +281,8 @@ static void extract_edituv_lines_iter_subdiv_mesh(const DRWSubdivCache *subdiv_c const MPoly *coarse_poly) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[coarse_poly - mr->mpoly]; + int *subdiv_loop_edge_index = (int *)GPU_vertbuf_get_data(subdiv_cache->edges_orig_index); uint start_loop_idx = subdiv_quad_index * 4; @@ -298,7 +293,7 @@ static void extract_edituv_lines_iter_subdiv_mesh(const DRWSubdivCache *subdiv_c (mr->e_origindex == nullptr || mr->e_origindex[edge_origindex] != ORIGINDEX_NONE)); edituv_edge_add(data, - (coarse_poly->flag & ME_HIDE) != 0 || !real_edge, + hidden || !real_edge, (coarse_poly->flag & ME_FACE_SEL) != 0, loop_idx, (loop_idx + 1 == end_loop_idx) ? start_loop_idx : (loop_idx + 1)); @@ -382,14 +377,15 @@ static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; const bool real_vert = !mr->v_origindex || mr->v_origindex[ml->v] != ORIGINDEX_NONE; - edituv_point_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); + edituv_point_add(data, hidden || !real_vert, (mp->flag & ME_FACE_SEL) != 0, ml_index); } } @@ -442,6 +438,7 @@ static void extract_edituv_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_ const MPoly *coarse_quad) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[coarse_quad - mr->mpoly]; int *subdiv_loop_vert_index = (int *)GPU_vertbuf_get_data(subdiv_cache->verts_orig_index); uint start_loop_idx = subdiv_quad_index * 4; @@ -450,10 +447,7 @@ static void extract_edituv_points_iter_subdiv_mesh(const DRWSubdivCache *subdiv_ const int vert_origindex = subdiv_loop_vert_index[i]; const bool real_vert = !mr->v_origindex || (vert_origindex != -1 && mr->v_origindex[vert_origindex] != ORIGINDEX_NONE); - edituv_point_add(data, - ((coarse_quad->flag & ME_HIDE) != 0) || !real_vert, - (coarse_quad->flag & ME_FACE_SEL) != 0, - i); + edituv_point_add(data, hidden || !real_vert, (coarse_quad->flag & ME_FACE_SEL) != 0, i); } } @@ -533,6 +527,8 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_EditUvElem_Data *data = static_cast<MeshExtract_EditUvElem_Data *>(_data); + const bool hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + if (mr->use_subsurf_fdots) { const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags; @@ -543,16 +539,13 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE); const bool subd_fdot = BLI_BITMAP_TEST(facedot_tags, ml->v); - edituv_facedot_add(data, - ((mp->flag & ME_HIDE) != 0) || !real_fdot || !subd_fdot, - (mp->flag & ME_FACE_SEL) != 0, - mp_index); + edituv_facedot_add( + data, hidden || !real_fdot || !subd_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } } else { const bool real_fdot = !mr->p_origindex || (mr->p_origindex[mp_index] != ORIGINDEX_NONE); - edituv_facedot_add( - data, ((mp->flag & ME_HIDE) != 0) || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); + edituv_facedot_add(data, hidden || !real_fdot, (mp->flag & ME_FACE_SEL) != 0, mp_index); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc index cc0b383f12b..8dc00617039 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_fdots.cc @@ -42,6 +42,8 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, const int mp_index, void *_userdata) { + const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_userdata); if (mr->use_subsurf_fdots) { const BLI_bitmap *facedot_tags = mr->me->runtime.subsurf_face_dot_tags; @@ -50,7 +52,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; - if (BLI_BITMAP_TEST(facedot_tags, ml->v) && !(mr->use_hide && (mp->flag & ME_HIDE))) { + if (BLI_BITMAP_TEST(facedot_tags, ml->v) && !hidden) { GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); return; } @@ -58,7 +60,7 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, GPU_indexbuf_set_point_restart(elb, mp_index); } else { - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { + if (!hidden) { GPU_indexbuf_set_point_vert(elb, mp_index, mp_index); } else { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc index e6c0d815963..fe883fb0c96 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines.cc @@ -58,14 +58,12 @@ static void extract_lines_iter_poly_mesh(const MeshRenderData *mr, GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); /* Using poly & loop iterator would complicate accessing the adjacent loop. */ const MLoop *mloop = mr->mloop; - const MEdge *medge = mr->medge; if (mr->use_hide || (mr->extract_type == MR_EXTRACT_MAPPED) || (mr->e_origindex != nullptr)) { const int ml_index_last = mp->loopstart + (mp->totloop - 1); int ml_index = ml_index_last, ml_index_next = mp->loopstart; do { const MLoop *ml = &mloop[ml_index]; - const MEdge *med = &medge[ml->e]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || + if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[ml->e]) || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && (mr->e_origindex[ml->e] == ORIGINDEX_NONE)))) { GPU_indexbuf_set_line_verts(elb, ml->e, ml_index, ml_index_next); @@ -111,7 +109,7 @@ static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(data); const int l_index_offset = mr->edge_len + ledge_index; const int e_index = mr->ledges[ledge_index]; - if (!((mr->use_hide && (med->flag & ME_HIDE)) || + if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[med - mr->medge]) || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { const int l_index = mr->loop_len + ledge_index * 2; @@ -185,9 +183,14 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, switch (mr->extract_type) { case MR_EXTRACT_MESH: { - const MEdge *medge = mr->medge; - for (DRWSubdivLooseEdge edge : loose_edges) { - *flags_data++ = (medge[edge.coarse_edge_index].flag & ME_HIDE) != 0; + const bool *hide_vert = mr->hide_vert; + if (hide_vert) { + for (DRWSubdivLooseEdge edge : loose_edges) { + *flags_data++ = hide_vert[edge.coarse_edge_index]; + } + } + else { + MutableSpan<uint>(flags_data, loose_edges.size()).fill(0); } break; } @@ -199,18 +202,23 @@ static void extract_lines_loose_geom_subdiv(const DRWSubdivCache *subdiv_cache, } } else { - for (DRWSubdivLooseEdge edge : loose_edges) { - int e = edge.coarse_edge_index; - - if (mr->e_origindex && mr->e_origindex[e] != ORIGINDEX_NONE) { - *flags_data++ = (mr->medge[mr->e_origindex[e]].flag & ME_HIDE) != 0; - } - else { - *flags_data++ = false; + const bool *hide_vert = mr->hide_vert; + if (hide_vert) { + for (DRWSubdivLooseEdge edge : loose_edges) { + int e = edge.coarse_edge_index; + + if (mr->e_origindex && mr->e_origindex[e] != ORIGINDEX_NONE) { + *flags_data++ = hide_vert[edge.coarse_edge_index]; + } + else { + *flags_data++ = false; + } } } + else { + MutableSpan<uint>(flags_data, loose_edges.size()).fill(0); + } } - break; } case MR_EXTRACT_BMESH: { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc index c2cfb66ec28..d6c246c51a9 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_adjacency.cc @@ -119,16 +119,17 @@ static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_LineAdjacency_Data *data = static_cast<MeshExtract_LineAdjacency_Data *>(_data); - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, - mr->mloop[mlt->tri[1]].v, - mr->mloop[mlt->tri[2]].v, - mlt->tri[0], - mlt->tri[1], - mlt->tri[2], - data); + const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mlt->poly]; + if (hidden) { + return; } + lines_adjacency_triangle(mr->mloop[mlt->tri[0]].v, + mr->mloop[mlt->tri[1]].v, + mr->mloop[mlt->tri[2]].v, + mlt->tri[0], + mlt->tri[1], + mlt->tri[2], + data); } static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc index 11c71d61775..d5f31c08eaf 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc @@ -47,8 +47,7 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, const MLoop *ml = &mloop[ml_index]; const int e_index = ml->e; - const MEdge *me = &mr->medge[e_index]; - if (!((mr->use_hide && (me->flag & ME_HIDE)) || + if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[e_index]) || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && (mr->e_origindex[e_index] == ORIGINDEX_NONE)))) { @@ -122,8 +121,7 @@ static void extract_lines_paint_mask_iter_subdiv_mesh(const DRWSubdivCache *subd GPU_indexbuf_set_line_restart(&data->elb, subdiv_edge_index); } else { - const MEdge *me = &mr->medge[coarse_edge_index]; - if (!((mr->use_hide && (me->flag & ME_HIDE)) || + if (!((mr->use_hide && mr->hide_edge && mr->hide_edge[coarse_edge_index]) || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->e_origindex) && (mr->e_origindex[coarse_edge_index] == ORIGINDEX_NONE)))) { const uint ml_index_other = (loop_idx == (end_loop_idx - 1)) ? start_loop_idx : diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc index f7c5505422b..ca46a38823d 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_points.cc @@ -43,10 +43,10 @@ BLI_INLINE void vert_set_mesh(GPUIndexBufBuilder *elb, const int v_index, const int l_index) { - const MVert *mv = &mr->mvert[v_index]; - if (!((mr->use_hide && (mv->flag & ME_HIDE)) || - ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && - (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) { + const bool hidden = mr->use_hide && mr->hide_vert && mr->hide_vert[v_index]; + + if (!(hidden || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && + (mr->v_origindex[v_index] == ORIGINDEX_NONE)))) { GPU_indexbuf_set_point_vert(elb, v_index, l_index); } else { @@ -181,8 +181,7 @@ static void extract_points_iter_subdiv_common(GPUIndexBufBuilder *elb, } } else { - const MVert *mv = &mr->mvert[coarse_vertex_index]; - if (mr->use_hide && (mv->flag & ME_HIDE)) { + if (mr->use_hide && mr->hide_vert && mr->hide_vert[coarse_vertex_index]) { GPU_indexbuf_set_point_restart(elb, coarse_vertex_index); continue; } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc index 9fc18620d11..2e3e6c7b6b1 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_ibo_tris.cc @@ -189,12 +189,12 @@ static void extract_tris_single_mat_iter_looptri_mesh(const MeshRenderData *mr, void *_data) { GPUIndexBufBuilder *elb = static_cast<GPUIndexBufBuilder *>(_data); - const MPoly *mp = &mr->mpoly[mlt->poly]; - if (!(mr->use_hide && (mp->flag & ME_HIDE))) { - GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]); + const bool hidden = mr->use_hide && mr->hide_poly && mr->hide_poly[mlt->poly]; + if (hidden) { + GPU_indexbuf_set_tri_restart(elb, mlt_index); } else { - GPU_indexbuf_set_tri_restart(elb, mlt_index); + GPU_indexbuf_set_tri_verts(elb, mlt_index, mlt->tri[0], mlt->tri[1], mlt->tri[2]); } } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc index 7f16837022c..64ade020418 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -58,7 +58,6 @@ template<typename AttributeType, typename VBOType> struct AttributeTypeConverter } }; -/* Similar to the one in #extract_mesh_vcol_vbo.cc */ struct gpuMeshCol { ushort r, g, b, a; }; diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc index ac517269e7d..ef67e1b540d 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_lnor.cc @@ -62,6 +62,8 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, const int mp_index, void *data) { + const bool hidden = mr->hide_poly && mr->hide_poly[mp_index]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { @@ -80,8 +82,8 @@ static void extract_lnor_iter_poly_mesh(const MeshRenderData *mr, /* Flag for paint mode overlay. * Only use MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. * In paint mode it will use the un-mapped data to draw the wire-frame. */ - if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && - (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { + if (hidden || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && + mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { lnor_data->w = -1; } else if (mp->flag & ME_FACE_SEL) { @@ -185,6 +187,8 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, const int mp_index, void *data) { + const bool hidden = mr->hide_poly && mr->hide_poly[mp_index]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { @@ -203,8 +207,8 @@ static void extract_lnor_hq_iter_poly_mesh(const MeshRenderData *mr, /* Flag for paint mode overlay. * Only use #MR_EXTRACT_MAPPED in edit mode where it is used to display the edge-normals. * In paint mode it will use the un-mapped data to draw the wire-frame. */ - if (mp->flag & ME_HIDE || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && - (mr->v_origindex) && mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { + if (hidden || (mr->edit_bmesh && mr->extract_type == MR_EXTRACT_MAPPED && (mr->v_origindex) && + mr->v_origindex[ml->v] == ORIGINDEX_NONE)) { lnor_data->w = -1; } else if (mp->flag & ME_FACE_SEL) { diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc index 9788beabeb5..313838be9e8 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_pos_nor.cc @@ -83,10 +83,11 @@ static void extract_pos_nor_iter_poly_bm(const MeshRenderData *mr, static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, const MPoly *mp, - const int UNUSED(mp_index), + const int mp_index, void *_data) { MeshExtract_PosNor_Data *data = static_cast<MeshExtract_PosNor_Data *>(_data); + const bool poly_hidden = mr->hide_poly && mr->hide_poly[mp_index]; const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; @@ -95,10 +96,11 @@ static void extract_pos_nor_iter_poly_mesh(const MeshRenderData *mr, PosNorLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; + const bool vert_hidden = mr->hide_vert && mr->hide_vert[ml->v]; copy_v3_v3(vert->pos, mv->co); vert->nor = data->normals[ml->v].low; /* Flag for paint mode overlay. */ - if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || + if (poly_hidden || vert_hidden || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { vert->nor.w = -1; @@ -432,18 +434,21 @@ static void extract_pos_nor_hq_iter_poly_mesh(const MeshRenderData *mr, void *_data) { MeshExtract_PosNorHQ_Data *data = static_cast<MeshExtract_PosNorHQ_Data *>(_data); + const bool poly_hidden = mr->hide_poly && mr->hide_poly[mp - mr->mpoly]; + const MLoop *mloop = mr->mloop; const int ml_index_end = mp->loopstart + mp->totloop; for (int ml_index = mp->loopstart; ml_index < ml_index_end; ml_index += 1) { const MLoop *ml = &mloop[ml_index]; + const bool vert_hidden = mr->hide_vert && mr->hide_vert[ml->v]; PosNorHQLoop *vert = &data->vbo_data[ml_index]; const MVert *mv = &mr->mvert[ml->v]; copy_v3_v3(vert->pos, mv->co); copy_v3_v3_short(vert->nor, data->normals[ml->v].high); /* Flag for paint mode overlay. */ - if (mp->flag & ME_HIDE || mv->flag & ME_HIDE || + if (poly_hidden || vert_hidden || ((mr->extract_type == MR_EXTRACT_MAPPED) && (mr->v_origindex) && (mr->v_origindex[ml->v] == ORIGINDEX_NONE))) { vert->nor[3] = -1; diff --git a/source/blender/draw/intern/shaders/common_aabb_lib.glsl b/source/blender/draw/intern/shaders/common_aabb_lib.glsl new file mode 100644 index 00000000000..b5f664a6779 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_aabb_lib.glsl @@ -0,0 +1,59 @@ + +#pragma BLENDER_REQUIRE(common_shape_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Axis Aligned Bound Box + * \{ */ + +struct AABB { + vec3 min, max; +}; + +AABB aabb_init_min_max() +{ + AABB aabb; + aabb.min = vec3(1.0e30); + aabb.max = vec3(-1.0e30); + return aabb; +} + +void aabb_merge(inout AABB aabb, vec3 v) +{ + aabb.min = min(aabb.min, v); + aabb.max = max(aabb.max, v); +} + +/** + * Return true if there is any intersection. + */ +bool aabb_intersect(AABB a, AABB b) +{ + return all(greaterThanEqual(min(a.max, b.max), max(a.min, b.min))); +} + +/** + * Compute intersect intersection volume of \a a and \a b. + * Return true if the resulting volume is not empty. + */ +bool aabb_clip(AABB a, AABB b, out AABB c) +{ + c.min = max(a.min, b.min); + c.max = min(a.max, b.max); + return all(greaterThanEqual(c.max, c.min)); +} + +Box aabb_to_box(AABB aabb) +{ + Box box; + box.corners[0] = aabb.min; + box.corners[1] = vec3(aabb.max.x, aabb.min.y, aabb.min.z); + box.corners[2] = vec3(aabb.max.x, aabb.max.y, aabb.min.z); + box.corners[3] = vec3(aabb.min.x, aabb.max.y, aabb.min.z); + box.corners[4] = vec3(aabb.min.x, aabb.min.y, aabb.max.z); + box.corners[5] = vec3(aabb.max.x, aabb.min.y, aabb.max.z); + box.corners[6] = aabb.max; + box.corners[7] = vec3(aabb.min.x, aabb.max.y, aabb.max.z); + return box; +} + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl new file mode 100644 index 00000000000..5f795d3abdb --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_draw_lib.glsl @@ -0,0 +1,216 @@ + +/** + * Debugging drawing library + * + * Quick way to draw debug geometry. All input should be in world space and + * will be rendered in the default view. No additional setup required. + **/ + +/** Global switch option. */ +bool drw_debug_draw_enable = true; +const vec4 drw_debug_default_color = vec4(1.0, 0.0, 0.0, 1.0); + +/* -------------------------------------------------------------------- */ +/** \name Internals + * \{ */ + +uint drw_debug_start_draw(uint v_needed) +{ + uint vertid = atomicAdd(drw_debug_draw_v_count, v_needed); + /* NOTE: Skip the header manually. */ + vertid += 1; + return vertid; +} + +uint drw_debug_color_pack(vec4 color) +{ + color = clamp(color, 0.0, 1.0); + uint result = 0; + result |= uint(color.x * 255.0) << 0u; + result |= uint(color.y * 255.0) << 8u; + result |= uint(color.z * 255.0) << 16u; + result |= uint(color.w * 255.0) << 24u; + return result; +} + +void drw_debug_line(inout uint vertid, vec3 v1, vec3 v2, uint color) +{ + drw_debug_verts_buf[vertid++] = DRWDebugVert( + floatBitsToUint(v1.x), floatBitsToUint(v1.y), floatBitsToUint(v1.z), color); + drw_debug_verts_buf[vertid++] = DRWDebugVert( + floatBitsToUint(v2.x), floatBitsToUint(v2.y), floatBitsToUint(v2.z), color); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name API + * \{ */ + +/** + * Draw a line. + */ +void drw_debug_line(vec3 v1, vec3 v2, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const uint v_needed = 2; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + drw_debug_line(vertid, v1, v2, drw_debug_color_pack(color)); + } +} +void drw_debug_line(vec3 v1, vec3 v2) +{ + drw_debug_line(v1, v2, drw_debug_default_color); +} + +/** + * Draw a quad contour. + */ +void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const uint v_needed = 8; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + drw_debug_line(vertid, v1, v2, pcolor); + drw_debug_line(vertid, v2, v3, pcolor); + drw_debug_line(vertid, v3, v4, pcolor); + drw_debug_line(vertid, v4, v1, pcolor); + } +} +void drw_debug_quad(vec3 v1, vec3 v2, vec3 v3, vec3 v4) +{ + drw_debug_quad(v1, v2, v3, v4, drw_debug_default_color); +} + +/** + * Draw a point as octahedron wireframe. + */ +void drw_debug_point(vec3 p, float radius, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + vec3 c = vec3(radius, -radius, 0); + vec3 v1 = p + c.xzz; + vec3 v2 = p + c.zxz; + vec3 v3 = p + c.yzz; + vec3 v4 = p + c.zyz; + vec3 v5 = p + c.zzx; + vec3 v6 = p + c.zzy; + + const uint v_needed = 12 * 2; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + drw_debug_line(vertid, v1, v2, pcolor); + drw_debug_line(vertid, v2, v3, pcolor); + drw_debug_line(vertid, v3, v4, pcolor); + drw_debug_line(vertid, v4, v1, pcolor); + drw_debug_line(vertid, v1, v5, pcolor); + drw_debug_line(vertid, v2, v5, pcolor); + drw_debug_line(vertid, v3, v5, pcolor); + drw_debug_line(vertid, v4, v5, pcolor); + drw_debug_line(vertid, v1, v6, pcolor); + drw_debug_line(vertid, v2, v6, pcolor); + drw_debug_line(vertid, v3, v6, pcolor); + drw_debug_line(vertid, v4, v6, pcolor); + } +} +void drw_debug_point(vec3 p, float radius) +{ + drw_debug_point(p, radius, drw_debug_default_color); +} +void drw_debug_point(vec3 p) +{ + drw_debug_point(p, 0.01); +} + +/** + * Draw a sphere wireframe as 3 axes circle. + */ +void drw_debug_sphere(vec3 p, float radius, vec4 color) +{ + if (!drw_debug_draw_enable) { + return; + } + const int circle_resolution = 16; + const uint v_needed = circle_resolution * 2 * 3; + uint vertid = drw_debug_start_draw(v_needed); + if (vertid + v_needed < DRW_DEBUG_DRAW_VERT_MAX) { + uint pcolor = drw_debug_color_pack(color); + for (int axis = 0; axis < 3; axis++) { + for (int edge = 0; edge < circle_resolution; edge++) { + float angle1 = (2.0 * 3.141592) * float(edge + 0) / float(circle_resolution); + vec3 p1 = vec3(cos(angle1), sin(angle1), 0.0) * radius; + p1 = vec3(p1[(0 + axis) % 3], p1[(1 + axis) % 3], p1[(2 + axis) % 3]); + + float angle2 = (2.0 * 3.141592) * float(edge + 1) / float(circle_resolution); + vec3 p2 = vec3(cos(angle2), sin(angle2), 0.0) * radius; + p2 = vec3(p2[(0 + axis) % 3], p2[(1 + axis) % 3], p2[(2 + axis) % 3]); + + drw_debug_line(vertid, p + p1, p + p2, pcolor); + } + } + } +} +void drw_debug_sphere(vec3 p, float radius) +{ + drw_debug_sphere(p, radius, drw_debug_default_color); +} + +/** + * Draw a matrix transformation as 3 colored axes. + */ +void drw_debug_matrix(mat4 mat, vec4 color) +{ + vec4 p[4] = vec4[4](vec4(0, 0, 0, 1), vec4(1, 0, 0, 1), vec4(0, 1, 0, 1), vec4(0, 0, 1, 1)); + for (int i = 0; i < 4; i++) { + p[i] = mat * p[i]; + p[i].xyz /= p[i].w; + } + drw_debug_line(p[0].xyz, p[0].xyz, vec4(1, 0, 0, 1)); + drw_debug_line(p[0].xyz, p[1].xyz, vec4(0, 1, 0, 1)); + drw_debug_line(p[0].xyz, p[2].xyz, vec4(0, 0, 1, 1)); +} +void drw_debug_matrix(mat4 mat) +{ + drw_debug_matrix(mat, drw_debug_default_color); +} + +/** + * Draw a matrix as a 2 units length bounding box, centered on origin. + */ +void drw_debug_matrix_as_bbox(mat4 mat, vec4 color) +{ + vec4 p[8] = vec4[8](vec4(-1, -1, -1, 1), + vec4(1, -1, -1, 1), + vec4(1, 1, -1, 1), + vec4(-1, 1, -1, 1), + vec4(-1, -1, 1, 1), + vec4(1, -1, 1, 1), + vec4(1, 1, 1, 1), + vec4(-1, 1, 1, 1)); + for (int i = 0; i < 8; i++) { + p[i] = mat * p[i]; + p[i].xyz /= p[i].w; + } + drw_debug_quad(p[0].xyz, p[1].xyz, p[2].xyz, p[3].xyz, color); + drw_debug_line(p[0].xyz, p[4].xyz, color); + drw_debug_line(p[1].xyz, p[5].xyz, color); + drw_debug_line(p[2].xyz, p[6].xyz, color); + drw_debug_line(p[3].xyz, p[7].xyz, color); + drw_debug_quad(p[4].xyz, p[5].xyz, p[6].xyz, p[7].xyz, color); +} +void drw_debug_matrix_as_bbox(mat4 mat) +{ + drw_debug_matrix_as_bbox(mat, drw_debug_default_color); +} + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_debug_print_lib.glsl b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl new file mode 100644 index 00000000000..0c7f32bd00d --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_print_lib.glsl @@ -0,0 +1,389 @@ + +/** + * Debug print implementation for shaders. + * + * `print()`: + * Log variable or strings inside the viewport. + * Using a unique non string argument will print the variable name with it. + * Concatenate by using multiple arguments. i.e: `print("Looped ", n, "times.")`. + * `drw_print_no_endl()`: + * Same as `print()` but does not finish the line. + * `drw_print_value()`: + * Display only the value of a variable. Does not finish the line. + * `drw_print_value_hex()`: + * Display only the hex representation of a variable. Does not finish the line. + * `drw_print_value_binary()`: Display only the binary representation of a + * variable. Does not finish the line. + * + * IMPORTANT: As it is now, it is not yet thread safe. Only print from one thread. You can use the + * IS_DEBUG_MOUSE_FRAGMENT macro in fragment shader to filter using mouse position or + * IS_FIRST_INVOCATION in compute shaders. + * + * NOTE: Floating point representation might not be very precise (see drw_print_value(float)). + * + * IMPORTANT: Multipler drawcalls can write to the buffer in sequence (if they are from different + * shgroups). However, we add barriers to support this case and it might change the application + * behavior. Uncomment DISABLE_DEBUG_SHADER_drw_print_BARRIER to remove the barriers if that + * happens. But then you are limited to a single invocation output. + * + * IMPORTANT: All of these are copied to the CPU debug libs (draw_debug.cc). They need to be kept + * in sync to write the same data. + */ + +/** Global switch option when you want to silence all prints from all shaders at once. */ +bool drw_debug_print_enable = true; + +/* Set drw_print_col to max value so we will start by creating a new line and get the correct + * threadsafe row. */ +uint drw_print_col = DRW_DEBUG_PRINT_WORD_WRAP_COLUMN; +uint drw_print_row = 0u; + +void drw_print_newline() +{ + if (!drw_debug_print_enable) { + return; + } + drw_print_col = 0u; + drw_print_row = atomicAdd(drw_debug_print_row_shared, 1u) + 1u; +} + +void drw_print_string_start(uint len) +{ + if (!drw_debug_print_enable) { + return; + } + /* Break before word. */ + if (drw_print_col + len > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + drw_print_newline(); + } +} + +void drw_print_char4(uint data) +{ + if (!drw_debug_print_enable) { + return; + } + /* Convert into char stream. */ + for (; data != 0u; data >>= 8u) { + uint char1 = data & 0xFFu; + /* Check for null terminator. */ + if (char1 == 0x00) { + break; + } + uint cursor = atomicAdd(drw_debug_print_cursor, 1u); + /* NOTE: Skip the header manually. */ + cursor += 4; + if (cursor < DRW_DEBUG_PRINT_MAX) { + /* For future usage. (i.e: Color) */ + uint flags = 0u; + uint col = drw_print_col++; + uint drw_print_header = (flags << 24u) | (drw_print_row << 16u) | (col << 8u); + drw_debug_print_buf[cursor] = drw_print_header | char1; + /* Break word. */ + if (drw_print_col > DRW_DEBUG_PRINT_WORD_WRAP_COLUMN) { + drw_print_newline(); + } + } + } +} + +/** + * NOTE(fclem): Strange behavior emerge when trying to increment the digit + * counter inside the append function. It looks like the compiler does not see + * it is referenced as an index for char4 and thus do not capture the right + * reference. I do not know if this is undefined behavior. As a matter of + * precaution, we implement all the append function separately. This behavior + * was observed on both Mesa & amdgpu-pro. + */ +/* Using ascii char code. Expect char1 to be less or equal to 0xFF. Appends chars to the right. */ +void drw_print_append_char(uint char1, inout uint char4) +{ + char4 = (char4 << 8u) | char1; +} + +void drw_print_append_digit(uint digit, inout uint char4) +{ + const uint char_A = 0x41u; + const uint char_0 = 0x30u; + bool is_hexadecimal = digit > 9u; + char4 = (char4 << 8u) | (is_hexadecimal ? (char_A + digit - 10u) : (char_0 + digit)); +} + +void drw_print_append_space(inout uint char4) +{ + char4 = (char4 << 8u) | 0x20u; +} + +void drw_print_value_binary(uint value) +{ + drw_print_no_endl("0b"); + drw_print_string_start(10u * 4u); + uint digits[10] = uint[10](0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u); + uint digit = 0u; + for (uint i = 0u; i < 32u; i++) { + drw_print_append_digit(((value >> i) & 1u), digits[digit / 4u]); + digit++; + if ((i % 4u) == 3u) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 9; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value_binary(int value) +{ + drw_print_value_binary(uint(value)); +} + +void drw_print_value_binary(float value) +{ + drw_print_value_binary(floatBitsToUint(value)); +} + +void drw_print_value_uint(uint value, const bool hex, bool is_negative, const bool is_unsigned) +{ + drw_print_string_start(3u * 4u); + const uint blank_value = hex ? 0x30303030u : 0x20202020u; + const uint prefix = hex ? 0x78302020u : 0x20202020u; + uint digits[3] = uint[3](blank_value, blank_value, prefix); + const uint base = hex ? 16u : 10u; + uint digit = 0u; + /* Add `u` suffix. */ + if (is_unsigned) { + drw_print_append_char('u', digits[digit / 4u]); + digit++; + } + /* Number's digits. */ + for (; value != 0u || digit == uint(is_unsigned); value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Add negative sign. */ + if (is_negative) { + drw_print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value_hex(uint value) +{ + drw_print_value_uint(value, true, false, false); +} + +void drw_print_value_hex(int value) +{ + drw_print_value_uint(uint(value), true, false, false); +} + +void drw_print_value_hex(float value) +{ + drw_print_value_uint(floatBitsToUint(value), true, false, false); +} + +void drw_print_value(uint value) +{ + drw_print_value_uint(value, false, false, true); +} + +void drw_print_value(int value) +{ + drw_print_value_uint(uint(abs(value)), false, (value < 0), false); +} + +void drw_print_value(bool value) +{ + if (value) { + drw_print_no_endl("true "); + } + else { + drw_print_no_endl("false"); + } +} + +/* NOTE(@fclem): This is homebrew and might not be 100% accurate (accuracy has + * not been tested and might dependent on compiler implementation). If unsure, + * use drw_print_value_hex and transcribe the value manually with another tool. */ +void drw_print_value(float val) +{ + /* We pad the string to match normal float values length. */ + if (isnan(val)) { + drw_print_no_endl(" NaN"); + return; + } + if (isinf(val)) { + if (sign(val) < 0.0) { + drw_print_no_endl(" -Inf"); + } + else { + drw_print_no_endl(" Inf"); + } + return; + } + + /* Adjusted for significant digits (6) with sign (1), decimal separator (1) + * and exponent (4). */ + const float significant_digits = 6.0; + drw_print_string_start(3u * 4u); + uint digits[3] = uint[3](0x20202020u, 0x20202020u, 0x20202020u); + + float exponent = floor(log(abs(val)) / log(10.0)); + bool display_exponent = exponent >= (significant_digits) || + exponent <= (-significant_digits + 1.0); + + float int_significant_digits = min(exponent + 1.0, significant_digits); + float dec_significant_digits = max(0.0, significant_digits - int_significant_digits); + /* Power to get to the rounding point. */ + float rounding_power = dec_significant_digits; + + if (val == 0.0 || isinf(exponent)) { + display_exponent = false; + int_significant_digits = dec_significant_digits = 1.0; + } + /* Remap to keep significant numbers count. */ + if (display_exponent) { + int_significant_digits = 1.0; + dec_significant_digits = significant_digits - int_significant_digits; + rounding_power = -exponent + dec_significant_digits; + } + /* Round at the last significant digit. */ + val = round(val * pow(10.0, rounding_power)); + /* Get back to final exponent. */ + val *= pow(10.0, -dec_significant_digits); + + float int_part; + float dec_part = modf(val, int_part); + + dec_part *= pow(10.0, dec_significant_digits); + + const uint base = 10u; + uint digit = 0u; + /* Exponent */ + uint value = uint(abs(exponent)); + if (display_exponent) { + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Exponent sign. */ + uint sign_char = (exponent < 0.0) ? '-' : '+'; + drw_print_append_char(sign_char, digits[digit / 4u]); + digit++; + /* Exponent `e` suffix. */ + drw_print_append_char(0x65u, digits[digit / 4u]); + digit++; + } + /* Decimal part. */ + value = uint(abs(dec_part)); +#if 0 /* We don't do that because it makes unstable values really hard to \ + read. */ + /* Trim trailing zeros. */ + while ((value % base) == 0u) { + value /= base; + if (value == 0u) { + break; + } + } +#endif + if (value != 0u) { + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Point separator. */ + drw_print_append_char('.', digits[digit / 4u]); + digit++; + } + /* Integer part. */ + value = uint(abs(int_part)); + for (int i = 0; value != 0u || i == 0; i++, value /= base) { + drw_print_append_digit(value % base, digits[digit / 4u]); + digit++; + } + /* Negative sign. */ + if (val < 0.0) { + drw_print_append_char('-', digits[digit / 4u]); + digit++; + } + /* Need to pad to uint alignment because we are issuing chars in "reverse". */ + for (uint i = digit % 4u; i < 4u && i > 0u; i++) { + drw_print_append_space(digits[digit / 4u]); + digit++; + } + /* Numbers are written from right to left. So we need to reverse the order. */ + for (int j = 2; j >= 0; j--) { + drw_print_char4(digits[j]); + } +} + +void drw_print_value(vec2 value) +{ + drw_print_no_endl("vec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(vec3 value) +{ + drw_print_no_endl("vec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(vec4 value) +{ + drw_print_no_endl("vec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(ivec2 value) +{ + drw_print_no_endl("ivec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(ivec3 value) +{ + drw_print_no_endl("ivec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(ivec4 value) +{ + drw_print_no_endl("ivec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(uvec2 value) +{ + drw_print_no_endl("uvec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(uvec3 value) +{ + drw_print_no_endl("uvec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(uvec4 value) +{ + drw_print_no_endl("uvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} + +void drw_print_value(bvec2 value) +{ + drw_print_no_endl("bvec2(", value[0], ", ", value[1], ")"); +} + +void drw_print_value(bvec3 value) +{ + drw_print_no_endl("bvec3(", value[0], ", ", value[1], ", ", value[1], ")"); +} + +void drw_print_value(bvec4 value) +{ + drw_print_no_endl("bvec4(", value[0], ", ", value[1], ", ", value[2], ", ", value[3], ")"); +} diff --git a/source/blender/draw/intern/shaders/common_debug_shape_lib.glsl b/source/blender/draw/intern/shaders/common_debug_shape_lib.glsl new file mode 100644 index 00000000000..538c55ce544 --- /dev/null +++ b/source/blender/draw/intern/shaders/common_debug_shape_lib.glsl @@ -0,0 +1,57 @@ + +/** + * Debug drawing of shapes. + */ + +#pragma BLENDER_REQUIRE(common_debug_draw_lib.glsl) +#pragma BLENDER_REQUIRE(common_shape_lib.glsl) + +void drw_debug(Box shape, vec4 color) +{ + drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color); + drw_debug_line(shape.corners[0], shape.corners[4], color); + drw_debug_line(shape.corners[1], shape.corners[5], color); + drw_debug_line(shape.corners[2], shape.corners[6], color); + drw_debug_line(shape.corners[3], shape.corners[7], color); + drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color); +} +void drw_debug(Box shape) +{ + drw_debug(shape, drw_debug_default_color); +} + +void drw_debug(Frustum shape, vec4 color) +{ + drw_debug_quad(shape.corners[0], shape.corners[1], shape.corners[2], shape.corners[3], color); + drw_debug_line(shape.corners[0], shape.corners[4], color); + drw_debug_line(shape.corners[1], shape.corners[5], color); + drw_debug_line(shape.corners[2], shape.corners[6], color); + drw_debug_line(shape.corners[3], shape.corners[7], color); + drw_debug_quad(shape.corners[4], shape.corners[5], shape.corners[6], shape.corners[7], color); +} +void drw_debug(Frustum shape) +{ + drw_debug(shape, drw_debug_default_color); +} + +void drw_debug(Pyramid shape, vec4 color) +{ + drw_debug_line(shape.corners[0], shape.corners[1], color); + drw_debug_line(shape.corners[0], shape.corners[2], color); + drw_debug_line(shape.corners[0], shape.corners[3], color); + drw_debug_line(shape.corners[0], shape.corners[4], color); + drw_debug_quad(shape.corners[1], shape.corners[2], shape.corners[3], shape.corners[4], color); +} +void drw_debug(Pyramid shape) +{ + drw_debug(shape, drw_debug_default_color); +} + +void drw_debug(Sphere shape, vec4 color) +{ + drw_debug_sphere(shape.center, shape.radius, color); +} +void drw_debug(Sphere shape) +{ + drw_debug(shape, drw_debug_default_color); +} diff --git a/source/blender/draw/intern/shaders/common_intersect_lib.glsl b/source/blender/draw/intern/shaders/common_intersect_lib.glsl new file mode 100644 index 00000000000..708d361029a --- /dev/null +++ b/source/blender/draw/intern/shaders/common_intersect_lib.glsl @@ -0,0 +1,399 @@ + +/** + * Intersection library used for culling. + * Results are meant to be conservative. + */ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) +#pragma BLENDER_REQUIRE(common_shape_lib.glsl) + +/* ---------------------------------------------------------------------- */ +/** \name Plane extraction functions. + * \{ */ + +/** \a v1 and \a v2 are vectors on the plane. \a p is a point on the plane. */ +vec4 isect_plane_setup(vec3 p, vec3 v1, vec3 v2) +{ + vec3 normal_to_plane = normalize(cross(v1, v2)); + return vec4(normal_to_plane, -dot(normal_to_plane, p)); +} + +struct IsectPyramid { + vec3 corners[5]; + vec4 planes[5]; +}; + +IsectPyramid isect_data_setup(Pyramid shape) +{ + vec3 A1 = shape.corners[1] - shape.corners[0]; + vec3 A2 = shape.corners[2] - shape.corners[0]; + vec3 A3 = shape.corners[3] - shape.corners[0]; + vec3 A4 = shape.corners[4] - shape.corners[0]; + vec3 S4 = shape.corners[4] - shape.corners[1]; + vec3 S2 = shape.corners[2] - shape.corners[1]; + + IsectPyramid data; + data.planes[0] = isect_plane_setup(shape.corners[0], A2, A1); + data.planes[1] = isect_plane_setup(shape.corners[0], A3, A2); + data.planes[2] = isect_plane_setup(shape.corners[0], A4, A3); + data.planes[3] = isect_plane_setup(shape.corners[0], A1, A4); + data.planes[4] = isect_plane_setup(shape.corners[1], S2, S4); + for (int i = 0; i < 5; i++) { + data.corners[i] = shape.corners[i]; + } + return data; +} + +struct IsectBox { + vec3 corners[8]; + vec4 planes[6]; +}; + +IsectBox isect_data_setup(Box shape) +{ + vec3 A1 = shape.corners[1] - shape.corners[0]; + vec3 A3 = shape.corners[3] - shape.corners[0]; + vec3 A4 = shape.corners[4] - shape.corners[0]; + + IsectBox data; + data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1); + data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3); + data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4); + /* Assumes that the box is actually a box! */ + data.planes[3] = vec4(-data.planes[0].xyz, -dot(-data.planes[0].xyz, shape.corners[6])); + data.planes[4] = vec4(-data.planes[1].xyz, -dot(-data.planes[1].xyz, shape.corners[6])); + data.planes[5] = vec4(-data.planes[2].xyz, -dot(-data.planes[2].xyz, shape.corners[6])); + for (int i = 0; i < 8; i++) { + data.corners[i] = shape.corners[i]; + } + return data; +} + +struct IsectFrustum { + vec3 corners[8]; + vec4 planes[6]; +}; + +IsectFrustum isect_data_setup(Frustum shape) +{ + vec3 A1 = shape.corners[1] - shape.corners[0]; + vec3 A3 = shape.corners[3] - shape.corners[0]; + vec3 A4 = shape.corners[4] - shape.corners[0]; + vec3 B5 = shape.corners[5] - shape.corners[6]; + vec3 B7 = shape.corners[7] - shape.corners[6]; + vec3 B2 = shape.corners[2] - shape.corners[6]; + + IsectFrustum data; + data.planes[0] = isect_plane_setup(shape.corners[0], A3, A1); + data.planes[1] = isect_plane_setup(shape.corners[0], A4, A3); + data.planes[2] = isect_plane_setup(shape.corners[0], A1, A4); + data.planes[3] = isect_plane_setup(shape.corners[6], B7, B5); + data.planes[4] = isect_plane_setup(shape.corners[6], B5, B2); + data.planes[5] = isect_plane_setup(shape.corners[6], B2, B7); + for (int i = 0; i < 8; i++) { + data.corners[i] = shape.corners[i]; + } + return data; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name View Intersection functions. + * \{ */ + +bool intersect_view(Pyramid pyramid) +{ + bool intersects = true; + + /* Do Pyramid vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 5; ++v) { + float test = dot(drw_view.frustum_planes[p], vec4(pyramid.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Frustum vertices vs Pyramid planes. */ + IsectPyramid i_pyramid = isect_data_setup(pyramid); + for (int p = 0; p < 5; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_pyramid.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + return intersects; +} + +bool intersect_view(Box box) +{ + bool intersects = true; + + /* Do Box vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(drw_view.frustum_planes[p], vec4(box.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Frustum vertices vs Box planes. */ + IsectBox i_box = isect_data_setup(box); + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_box.planes[p], vec4(drw_view.frustum_corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + return intersects; +} + +bool intersect_view(Sphere sphere) +{ + bool intersects = true; + + for (int p = 0; p < 6 && intersects; ++p) { + float dist_to_plane = dot(drw_view.frustum_planes[p], vec4(sphere.center, 1.0)); + if (dist_to_plane < -sphere.radius) { + intersects = false; + } + } + /* TODO reject false positive. */ + return intersects; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Shape vs. Shape Intersection functions. + * \{ */ + +bool intersect(IsectPyramid i_pyramid, Box box) +{ + bool intersects = true; + + /* Do Box vertices vs Pyramid planes. */ + for (int p = 0; p < 5; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_pyramid.planes[p], vec4(box.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Pyramid vertices vs Box planes. */ + IsectBox i_box = isect_data_setup(box); + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 5; ++v) { + float test = dot(i_box.planes[p], vec4(i_pyramid.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + return intersects; +} + +bool intersect(IsectFrustum i_frustum, Pyramid pyramid) +{ + bool intersects = true; + + /* Do Pyramid vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 5; ++v) { + float test = dot(i_frustum.planes[p], vec4(pyramid.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Frustum vertices vs Pyramid planes. */ + IsectPyramid i_pyramid = isect_data_setup(pyramid); + for (int p = 0; p < 5; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_pyramid.planes[p], vec4(i_frustum.corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + return intersects; +} + +bool intersect(IsectFrustum i_frustum, Box box) +{ + bool intersects = true; + + /* Do Box vertices vs Frustum planes. */ + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_frustum.planes[p], vec4(box.corners[v], 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + if (!intersects) { + return intersects; + } + + /* Now do Frustum vertices vs Box planes. */ + IsectBox i_box = isect_data_setup(box); + for (int p = 0; p < 6; ++p) { + bool is_any_vertex_on_positive_side = false; + for (int v = 0; v < 8; ++v) { + float test = dot(i_box.planes[p], vec4(i_frustum.corners[v].xyz, 1.0)); + if (test > 0.0) { + is_any_vertex_on_positive_side = true; + break; + } + } + bool all_vertex_on_negative_side = !is_any_vertex_on_positive_side; + if (all_vertex_on_negative_side) { + intersects = false; + break; + } + } + + return intersects; +} + +bool intersect(IsectFrustum i_frustum, Sphere sphere) +{ + bool intersects = true; + + for (int p = 0; p < 8; ++p) { + float dist_to_plane = dot(i_frustum.planes[p], vec4(sphere.center, 1.0)); + if (dist_to_plane < -sphere.radius) { + intersects = false; + break; + } + } + return intersects; +} + +bool intersect(Cone cone, Sphere sphere) +{ + /** + * Following "Improve Tile-based Light Culling with Spherical-sliced Cone" + * by Eric Zhang + * https://lxjk.github.io/2018/03/25/Improve-Tile-based-Light-Culling-with-Spherical-sliced-Cone.html + */ + float sphere_distance = length(sphere.center); + float sphere_distance_rcp = safe_rcp(sphere_distance); + float sphere_sin = saturate(sphere.radius * sphere_distance_rcp); + float sphere_cos = sqrt(1.0 - sphere_sin * sphere_sin); + float cone_aperture_sin = sqrt(1.0 - cone.angle_cos * cone.angle_cos); + + float cone_sphere_center_cos = dot(sphere.center * sphere_distance_rcp, cone.direction); + /* cos(A+B) = cos(A) * cos(B) - sin(A) * sin(B). */ + float cone_sphere_angle_sum_cos = (sphere.radius > sphere_distance) ? + -1.0 : + (cone.angle_cos * sphere_cos - + cone_aperture_sin * sphere_sin); + /* Comparing cosines instead of angles since we are interested + * only in the monotonic region [0 .. M_PI / 2]. This saves costly acos() calls. */ + bool intersects = (cone_sphere_center_cos >= cone_sphere_angle_sum_cos); + + return intersects; +} + +bool intersect(Circle circle_a, Circle circle_b) +{ + return distance_squared(circle_a.center, circle_b.center) < + sqr(circle_a.radius + circle_b.radius); +} + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl index ae82277d9a6..71460c39285 100644 --- a/source/blender/draw/intern/shaders/common_math_geom_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_geom_lib.glsl @@ -5,11 +5,28 @@ /** \name Math intersection & projection functions. * \{ */ +vec4 plane_from_quad(vec3 v0, vec3 v1, vec3 v2, vec3 v3) +{ + vec3 nor = normalize(cross(v2 - v1, v0 - v1) + cross(v0 - v3, v2 - v3)); + return vec4(nor, -dot(nor, v2)); +} + +vec4 plane_from_tri(vec3 v0, vec3 v1, vec3 v2) +{ + vec3 nor = normalize(cross(v2 - v1, v0 - v1)); + return vec4(nor, -dot(nor, v2)); +} + float point_plane_projection_dist(vec3 line_origin, vec3 plane_origin, vec3 plane_normal) { return dot(plane_normal, plane_origin - line_origin); } +float point_line_projection_dist(vec2 point, vec2 line_origin, vec2 line_normal) +{ + return dot(line_normal, line_origin - point); +} + float line_plane_intersect_dist(vec3 line_origin, vec3 line_direction, vec3 plane_origin, @@ -104,6 +121,25 @@ float line_unit_box_intersect_dist_safe(vec3 line_origin, vec3 line_direction) } /** + * Same as line_unit_box_intersect_dist but for 2D case. + */ +float line_unit_square_intersect_dist(vec2 line_origin, vec2 line_direction) +{ + vec2 first_plane = (vec2(1.0) - line_origin) / line_direction; + vec2 second_plane = (vec2(-1.0) - line_origin) / line_direction; + vec2 farthest_plane = max(first_plane, second_plane); + + return min_v2(farthest_plane); +} + +float line_unit_square_intersect_dist_safe(vec2 line_origin, vec2 line_direction) +{ + vec2 safe_line_direction = max(vec2(1e-8), abs(line_direction)) * + select(vec2(1.0), -vec2(1.0), lessThan(line_direction, vec2(0.0))); + return line_unit_square_intersect_dist(line_origin, safe_line_direction); +} + +/** * Returns clipping distance (intersection with the nearest plane) with the given axis-aligned * bound box along \a line_direction. * Safe even if \a line_direction is degenerate. diff --git a/source/blender/draw/intern/shaders/common_math_lib.glsl b/source/blender/draw/intern/shaders/common_math_lib.glsl index 51f3c890df8..e081a0243ca 100644 --- a/source/blender/draw/intern/shaders/common_math_lib.glsl +++ b/source/blender/draw/intern/shaders/common_math_lib.glsl @@ -116,8 +116,8 @@ bool flag_test(int flag, int val) { return (flag & val) != 0; } void set_flag_from_test(inout uint value, bool test, uint flag) { if (test) { value |= flag; } else { value &= ~flag; } } void set_flag_from_test(inout int value, bool test, int flag) { if (test) { value |= flag; } else { value &= ~flag; } } -#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights))); -#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights))); +#define weighted_sum(val0, val1, val2, val3, weights) ((val0 * weights[0] + val1 * weights[1] + val2 * weights[2] + val3 * weights[3]) * safe_rcp(sum(weights))) +#define weighted_sum_array(val, weights) ((val[0] * weights[0] + val[1] * weights[1] + val[2] * weights[2] + val[3] * weights[3]) * safe_rcp(sum(weights))) /* clang-format on */ diff --git a/source/blender/draw/intern/shaders/common_shape_lib.glsl b/source/blender/draw/intern/shaders/common_shape_lib.glsl new file mode 100644 index 00000000000..f2c8bf0faaf --- /dev/null +++ b/source/blender/draw/intern/shaders/common_shape_lib.glsl @@ -0,0 +1,202 @@ + +#pragma BLENDER_REQUIRE(common_math_geom_lib.glsl) + +/** + * Geometric shape structures. + * Some constructors might seems redundant but are here to make the API cleaner and + * allow for more than one constructor per type. + */ + +/* ---------------------------------------------------------------------- */ +/** \name Circle + * \{ */ + +struct Circle { + vec2 center; + float radius; +}; + +Circle shape_circle(vec2 center, float radius) +{ + return Circle(center, radius); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Sphere + * \{ */ + +struct Sphere { + vec3 center; + float radius; +}; + +Sphere shape_sphere(vec3 center, float radius) +{ + return Sphere(center, radius); +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Box + * \{ */ + +struct Box { + vec3 corners[8]; +}; + +/* Construct box from 4 basis points. */ +Box shape_box(vec3 v000, vec3 v100, vec3 v010, vec3 v001) +{ + v100 -= v000; + v010 -= v000; + v001 -= v000; + Box box; + box.corners[0] = v000; + box.corners[1] = v000 + v100; + box.corners[2] = v000 + v010 + v100; + box.corners[3] = v000 + v010; + box.corners[4] = box.corners[0] + v001; + box.corners[5] = box.corners[1] + v001; + box.corners[6] = box.corners[2] + v001; + box.corners[7] = box.corners[3] + v001; + return box; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Square Pyramid + * \{ */ + +struct Pyramid { + /* Apex is the first. Base vertices are in clockwise order from front view. */ + vec3 corners[5]; +}; + +/** + * Regular Square Pyramid (can be oblique). + * Use this corner order. + * (Top-Down View of the pyramid) + * <pre> + * + * Y + * | + * | + * .-----X + * + * 4-----------3 + * | \ / | + * | \ / | + * | 0 | + * | / \ | + * | / \ | + * 1-----------2 + * </pre> + * base_corner_00 is vertex 1 + * base_corner_01 is vertex 2 + * base_corner_10 is vertex 4 + */ +Pyramid shape_pyramid(vec3 apex, vec3 base_corner_00, vec3 base_corner_01, vec3 base_corner_10) +{ + Pyramid pyramid; + pyramid.corners[0] = apex; + pyramid.corners[1] = base_corner_00; + pyramid.corners[2] = base_corner_01; + pyramid.corners[3] = base_corner_10 + (base_corner_01 - base_corner_00); + pyramid.corners[4] = base_corner_10; + return pyramid; +} + +/** + * Regular Square Pyramid. + * <pre> + * + * Y + * | + * | + * .-----X + * + * 4-----Y-----3 + * | \ | / | + * | \ | / | + * | 0-----X + * | / \ | + * | / \ | + * 1-----------2 + * </pre> + * base_center_pos_x is vector from base center to X + * base_center_pos_y is vector from base center to Y + */ +Pyramid shape_pyramid_non_oblique(vec3 apex, + vec3 base_center, + vec3 base_center_pos_x, + vec3 base_center_pos_y) +{ + Pyramid pyramid; + pyramid.corners[0] = apex; + pyramid.corners[1] = base_center - base_center_pos_x - base_center_pos_y; + pyramid.corners[2] = base_center + base_center_pos_x - base_center_pos_y; + pyramid.corners[3] = base_center + base_center_pos_x + base_center_pos_y; + pyramid.corners[4] = base_center - base_center_pos_x + base_center_pos_y; + return pyramid; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Frustum + * \{ */ + +struct Frustum { + vec3 corners[8]; +}; + +/** + * Use this corner order. + * <pre> + * + * Z Y + * | / + * |/ + * .-----X + * 2----------6 + * /| /| + * / | / | + * 1----------5 | + * | | | | + * | 3-------|--7 + * | / | / + * |/ |/ + * 0----------4 + * </pre> + */ +Frustum shape_frustum(vec3 corners[8]) +{ + Frustum frustum; + for (int i = 0; i < 8; i++) { + frustum.corners[i] = corners[i]; + } + return frustum; +} + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Cone + * \{ */ + +/* Cone at orign with no height. */ +struct Cone { + vec3 direction; + float angle_cos; +}; + +Cone shape_cone(vec3 direction, float angle_cosine) +{ + return Cone(direction, angle_cosine); +} + +/** \} */ diff --git a/source/blender/draw/intern/shaders/common_view_lib.glsl b/source/blender/draw/intern/shaders/common_view_lib.glsl index 8eecaa46b58..8ab2ef10e4c 100644 --- a/source/blender/draw/intern/shaders/common_view_lib.glsl +++ b/source/blender/draw/intern/shaders/common_view_lib.glsl @@ -37,6 +37,9 @@ layout(std140) uniform viewBlock # endif #endif +#define IS_DEBUG_MOUSE_FRAGMENT (ivec2(gl_FragCoord) == drw_view.mouse_pixel) +#define IS_FIRST_INVOCATION (gl_GlobalInvocationID == uvec3(0)) + #define ViewNear (ViewVecs[0].w) #define ViewFar (ViewVecs[1].w) diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl new file mode 100644 index 00000000000..3fc5294b024 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_frag.glsl @@ -0,0 +1,9 @@ + +/** + * Display debug edge list. + **/ + +void main() +{ + out_color = interp.color; +} diff --git a/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl new file mode 100644 index 00000000000..92c546aa203 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_draw_display_vert.glsl @@ -0,0 +1,15 @@ + +/** + * Display debug edge list. + **/ + +void main() +{ + /* Skip the first vertex containing header data. */ + DRWDebugVert vert = drw_debug_verts_buf[gl_VertexID + 1]; + vec3 pos = uintBitsToFloat(uvec3(vert.pos0, vert.pos1, vert.pos2)); + vec4 col = vec4((uvec4(vert.color) >> uvec4(0, 8, 16, 24)) & 0xFFu); + + interp.color = col; + gl_Position = persmat * vec4(pos, 1.0); +} diff --git a/source/blender/draw/intern/shaders/draw_debug_info.hh b/source/blender/draw/intern/shaders/draw_debug_info.hh new file mode 100644 index 00000000000..893a5e537d9 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_info.hh @@ -0,0 +1,52 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +/* -------------------------------------------------------------------- */ +/** \name Debug print + * + * Allows print() function to have logging support inside shaders. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_debug_print) + .typedef_source("draw_shader_shared.h") + .storage_buf(7, Qualifier::READ_WRITE, "uint", "drw_debug_print_buf[]"); + +GPU_SHADER_INTERFACE_INFO(draw_debug_print_display_iface, "").flat(Type::UINT, "char_index"); + +GPU_SHADER_CREATE_INFO(draw_debug_print_display) + .do_static_compilation(true) + .typedef_source("draw_shader_shared.h") + .storage_buf(7, Qualifier::READ, "uint", "drw_debug_print_buf[]") + .vertex_out(draw_debug_print_display_iface) + .fragment_out(0, Type::VEC4, "out_color") + .vertex_source("draw_debug_print_display_vert.glsl") + .fragment_source("draw_debug_print_display_frag.glsl") + .additional_info("draw_view"); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Debug draw shapes + * + * Allows to draw lines and points just like the DRW_debug module functions. + * \{ */ + +GPU_SHADER_CREATE_INFO(draw_debug_draw) + .typedef_source("draw_shader_shared.h") + .storage_buf(6, Qualifier::READ_WRITE, "DRWDebugVert", "drw_debug_verts_buf[]"); + +GPU_SHADER_INTERFACE_INFO(draw_debug_draw_display_iface, "interp").flat(Type::VEC4, "color"); + +GPU_SHADER_CREATE_INFO(draw_debug_draw_display) + .do_static_compilation(true) + .typedef_source("draw_shader_shared.h") + .storage_buf(6, Qualifier::READ, "DRWDebugVert", "drw_debug_verts_buf[]") + .vertex_out(draw_debug_draw_display_iface) + .fragment_out(0, Type::VEC4, "out_color") + .push_constant(Type::MAT4, "persmat") + .vertex_source("draw_debug_draw_display_vert.glsl") + .fragment_source("draw_debug_draw_display_frag.glsl") + .additional_info("draw_view"); + +/** \} */ diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl new file mode 100644 index 00000000000..fe608816109 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_frag.glsl @@ -0,0 +1,133 @@ + +/** + * Display characters using an ascii table. + **/ + +#pragma BLENDER_REQUIRE(common_math_lib.glsl) + +bool char_intersect(uvec2 bitmap_position) +{ + /* Using 8x8 = 64bits = uvec2. */ + uvec2 ascii_bitmap[96] = uvec2[96](uvec2(0x00000000u, 0x00000000u), + uvec2(0x18001800u, 0x183c3c18u), + uvec2(0x00000000u, 0x36360000u), + uvec2(0x7f363600u, 0x36367f36u), + uvec2(0x301f0c00u, 0x0c3e031eu), + uvec2(0x0c666300u, 0x00633318u), + uvec2(0x3b336e00u, 0x1c361c6eu), + uvec2(0x00000000u, 0x06060300u), + uvec2(0x060c1800u, 0x180c0606u), + uvec2(0x180c0600u, 0x060c1818u), + uvec2(0x3c660000u, 0x00663cffu), + uvec2(0x0c0c0000u, 0x000c0c3fu), + uvec2(0x000c0c06u, 0x00000000u), + uvec2(0x00000000u, 0x0000003fu), + uvec2(0x000c0c00u, 0x00000000u), + uvec2(0x06030100u, 0x6030180cu), + uvec2(0x6f673e00u, 0x3e63737bu), + uvec2(0x0c0c3f00u, 0x0c0e0c0cu), + uvec2(0x06333f00u, 0x1e33301cu), + uvec2(0x30331e00u, 0x1e33301cu), + uvec2(0x7f307800u, 0x383c3633u), + uvec2(0x30331e00u, 0x3f031f30u), + uvec2(0x33331e00u, 0x1c06031fu), + uvec2(0x0c0c0c00u, 0x3f333018u), + uvec2(0x33331e00u, 0x1e33331eu), + uvec2(0x30180e00u, 0x1e33333eu), + uvec2(0x000c0c00u, 0x000c0c00u), + uvec2(0x000c0c06u, 0x000c0c00u), + uvec2(0x060c1800u, 0x180c0603u), + uvec2(0x003f0000u, 0x00003f00u), + uvec2(0x180c0600u, 0x060c1830u), + uvec2(0x0c000c00u, 0x1e333018u), + uvec2(0x7b031e00u, 0x3e637b7bu), + uvec2(0x3f333300u, 0x0c1e3333u), + uvec2(0x66663f00u, 0x3f66663eu), + uvec2(0x03663c00u, 0x3c660303u), + uvec2(0x66361f00u, 0x1f366666u), + uvec2(0x16467f00u, 0x7f46161eu), + uvec2(0x16060f00u, 0x7f46161eu), + uvec2(0x73667c00u, 0x3c660303u), + uvec2(0x33333300u, 0x3333333fu), + uvec2(0x0c0c1e00u, 0x1e0c0c0cu), + uvec2(0x33331e00u, 0x78303030u), + uvec2(0x36666700u, 0x6766361eu), + uvec2(0x46667f00u, 0x0f060606u), + uvec2(0x6b636300u, 0x63777f7fu), + uvec2(0x73636300u, 0x63676f7bu), + uvec2(0x63361c00u, 0x1c366363u), + uvec2(0x06060f00u, 0x3f66663eu), + uvec2(0x3b1e3800u, 0x1e333333u), + uvec2(0x36666700u, 0x3f66663eu), + uvec2(0x38331e00u, 0x1e33070eu), + uvec2(0x0c0c1e00u, 0x3f2d0c0cu), + uvec2(0x33333f00u, 0x33333333u), + uvec2(0x331e0c00u, 0x33333333u), + uvec2(0x7f776300u, 0x6363636bu), + uvec2(0x1c366300u, 0x6363361cu), + uvec2(0x0c0c1e00u, 0x3333331eu), + uvec2(0x4c667f00u, 0x7f633118u), + uvec2(0x06061e00u, 0x1e060606u), + uvec2(0x30604000u, 0x03060c18u), + uvec2(0x18181e00u, 0x1e181818u), + uvec2(0x00000000u, 0x081c3663u), + uvec2(0x000000ffu, 0x00000000u), + uvec2(0x00000000u, 0x0c0c1800u), + uvec2(0x3e336e00u, 0x00001e30u), + uvec2(0x66663b00u, 0x0706063eu), + uvec2(0x03331e00u, 0x00001e33u), + uvec2(0x33336e00u, 0x3830303eu), + uvec2(0x3f031e00u, 0x00001e33u), + uvec2(0x06060f00u, 0x1c36060fu), + uvec2(0x333e301fu, 0x00006e33u), + uvec2(0x66666700u, 0x0706366eu), + uvec2(0x0c0c1e00u, 0x0c000e0cu), + uvec2(0x3033331eu, 0x30003030u), + uvec2(0x1e366700u, 0x07066636u), + uvec2(0x0c0c1e00u, 0x0e0c0c0cu), + uvec2(0x7f6b6300u, 0x0000337fu), + uvec2(0x33333300u, 0x00001f33u), + uvec2(0x33331e00u, 0x00001e33u), + uvec2(0x663e060fu, 0x00003b66u), + uvec2(0x333e3078u, 0x00006e33u), + uvec2(0x66060f00u, 0x00003b6eu), + uvec2(0x1e301f00u, 0x00003e03u), + uvec2(0x0c2c1800u, 0x080c3e0cu), + uvec2(0x33336e00u, 0x00003333u), + uvec2(0x331e0c00u, 0x00003333u), + uvec2(0x7f7f3600u, 0x0000636bu), + uvec2(0x1c366300u, 0x00006336u), + uvec2(0x333e301fu, 0x00003333u), + uvec2(0x0c263f00u, 0x00003f19u), + uvec2(0x0c0c3800u, 0x380c0c07u), + uvec2(0x18181800u, 0x18181800u), + uvec2(0x0c0c0700u, 0x070c0c38u), + uvec2(0x00000000u, 0x6e3b0000u), + uvec2(0x00000000u, 0x00000000u)); + + if (!in_range_inclusive(bitmap_position, uvec2(0), uvec2(7))) { + return false; + } + uint char_bits = ascii_bitmap[char_index][bitmap_position.y >> 2u & 1u]; + char_bits = (char_bits >> ((bitmap_position.y & 3u) * 8u + bitmap_position.x)); + return (char_bits & 1u) != 0u; +} + +void main() +{ + uvec2 bitmap_position = uvec2(gl_PointCoord.xy * 8.0); + /* Point coord start from top left corner. But layout is from bottom to top. */ + bitmap_position.y = 7 - bitmap_position.y; + + if (char_intersect(bitmap_position)) { + out_color = vec4(1); + } + else if (char_intersect(bitmap_position + uvec2(0, 1))) { + /* Shadow */ + out_color = vec4(0, 0, 0, 1); + } + else { + /* Transparent Background for ease of read. */ + out_color = vec4(0, 0, 0, 0.2); + } +}
\ No newline at end of file diff --git a/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl new file mode 100644 index 00000000000..c8fc3815436 --- /dev/null +++ b/source/blender/draw/intern/shaders/draw_debug_print_display_vert.glsl @@ -0,0 +1,29 @@ + +/** + * Display characters using an ascii table. Outputs one point per character. + **/ + +#pragma BLENDER_REQUIRE(common_view_lib.glsl) + +void main() +{ + /* Skip first 4 chars containing header data. */ + uint char_data = drw_debug_print_buf[gl_VertexID + 4]; + char_index = (char_data & 0xFFu) - 0x20u; + + /* Discard invalid chars. */ + if (char_index >= 96u) { + gl_Position = vec4(-1); + gl_PointSize = 0.0; + return; + } + uint row = (char_data >> 16u) & 0xFFu; + uint col = (char_data >> 8u) & 0xFFu; + + float char_size = 16.0; + /* Change anchor point to the top left. */ + vec2 pos_on_screen = char_size * vec2(col, row) + char_size * 4; + gl_Position = vec4( + pos_on_screen * drw_view.viewport_size_inverse * vec2(2.0, -2.0) - vec2(1.0, -1.0), 0, 1); + gl_PointSize = char_size; +}
\ No newline at end of file diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 3608140a29d..e7c7f679b16 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -402,6 +402,7 @@ static void draw_marker_name(const uchar *text_color, const uiFontStyle *fstyle, TimeMarker *marker, float marker_x, + float xmax, float text_y) { const char *name = marker->name; @@ -419,8 +420,16 @@ static void draw_marker_name(const uchar *text_color, } #endif - int name_x = marker_x + UI_DPI_ICON_SIZE * 0.6; - UI_fontstyle_draw_simple(fstyle, name_x, text_y, name, final_text_color); + const int icon_half_width = UI_DPI_ICON_SIZE * 0.6; + const struct uiFontStyleDraw_Params fs_params = {.align = UI_STYLE_TEXT_LEFT, .word_wrap = 0}; + const struct rcti rect = { + .xmin = marker_x + icon_half_width, + .xmax = xmax - icon_half_width, + .ymin = text_y, + .ymax = text_y, + }; + + UI_fontstyle_draw(fstyle, &rect, name, strlen(name), final_text_color, &fs_params); } static void draw_marker_line(const uchar *color, int xpos, int ymin, int ymax) @@ -462,8 +471,13 @@ static int marker_get_icon_id(TimeMarker *marker, int flag) return (marker->flag & SELECT) ? ICON_MARKER_HLT : ICON_MARKER; } -static void draw_marker( - const uiFontStyle *fstyle, TimeMarker *marker, int cfra, int xpos, int flag, int region_height) +static void draw_marker(const uiFontStyle *fstyle, + TimeMarker *marker, + int xpos, + int xmax, + int flag, + int region_height, + bool is_elevated) { uchar line_color[4], text_color[4]; @@ -479,12 +493,11 @@ static void draw_marker( GPU_blend(GPU_BLEND_NONE); float name_y = UI_DPI_FAC * 18; - /* Give an offset to the marker name when selected, - * or when near the current frame (5 frames range, starting from the current one). */ - if ((marker->flag & SELECT) || (cfra - 4 <= marker->frame && marker->frame <= cfra)) { + /* Give an offset to the marker that is elevated. */ + if (is_elevated) { name_y += UI_DPI_FAC * 10; } - draw_marker_name(text_color, fstyle, marker, xpos, name_y); + draw_marker_name(text_color, fstyle, marker, xpos, xmax, name_y); } static void draw_markers_background(rctf *rect) @@ -532,6 +545,14 @@ static void get_marker_clip_frame_range(View2D *v2d, float xscale, int r_range[2 r_range[1] = v2d->cur.xmax + font_width_max; } +static int markers_frame_sort(const void *a, const void *b) +{ + const TimeMarker *marker_a = a; + const TimeMarker *marker_b = b; + + return marker_a->frame > marker_b->frame; +} + void ED_markers_draw(const bContext *C, int flag) { ListBase *markers = ED_context_get_markers(C); @@ -561,22 +582,69 @@ void ED_markers_draw(const bContext *C, int flag) const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - /* Separate loops in order to draw selected markers on top */ - LISTBASE_FOREACH (TimeMarker *, marker, markers) { - if ((marker->flag & SELECT) == 0) { - if (marker_is_in_frame_range(marker, clip_frame_range)) { - draw_marker(fstyle, marker, cfra, marker->frame * xscale, flag, region->winy); - } + /* Markers are not stored by frame order, so we need to sort it here. */ + ListBase sorted_markers; + + BLI_duplicatelist(&sorted_markers, markers); + BLI_listbase_sort(&sorted_markers, markers_frame_sort); + + /** + * Set a temporary bit in the marker's flag to indicate that it should be elevated. + * This bit will be flipped back at the end of this function. + */ + const int ELEVATED = 0x10; + LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) { + const bool is_elevated = (marker->flag & SELECT) || + (cfra >= marker->frame && + (marker->next == NULL || cfra < marker->next->frame)); + SET_FLAG_FROM_TEST(marker->flag, is_elevated, ELEVATED); + } + + /* Separate loops in order to draw selected markers on top. */ + + /** + * Draw non-elevated markers first. + * Note that unlike the elevated markers, these marker names will always be clipped by the + * proceeding marker. This is done because otherwise, the text overlaps with the icon of the + * marker itself. + */ + LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) { + if ((marker->flag & ELEVATED) == 0 && marker_is_in_frame_range(marker, clip_frame_range)) { + const int xmax = marker->next ? marker->next->frame : clip_frame_range[1] + 1; + draw_marker( + fstyle, marker, marker->frame * xscale, xmax * xscale, flag, region->winy, false); } } - LISTBASE_FOREACH (TimeMarker *, marker, markers) { - if (marker->flag & SELECT) { - if (marker_is_in_frame_range(marker, clip_frame_range)) { - draw_marker(fstyle, marker, cfra, marker->frame * xscale, flag, region->winy); - } + + /* Now draw the elevated markers */ + for (TimeMarker *marker = sorted_markers.first; marker != NULL;) { + + /* Skip this marker if it is elevated or out of the frame range. */ + if ((marker->flag & ELEVATED) == 0 || !marker_is_in_frame_range(marker, clip_frame_range)) { + marker = marker->next; + continue; } + + /* Find the next elevated marker. */ + /* We use the next marker to determine how wide our text should be */ + TimeMarker *next_marker = marker->next; + while (next_marker != NULL && (next_marker->flag & ELEVATED) == 0) { + next_marker = next_marker->next; + } + + const int xmax = next_marker ? next_marker->frame : clip_frame_range[1] + 1; + draw_marker(fstyle, marker, marker->frame * xscale, xmax * xscale, flag, region->winy, true); + + marker = next_marker; } + /* Reset the elevated flag. */ + LISTBASE_FOREACH (TimeMarker *, marker, &sorted_markers) { + marker->flag &= ~ELEVATED; + } + + BLI_freelistN(&sorted_markers); + GPU_matrix_pop(); } diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 08d5d6558e0..ae15bc39ec8 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -59,7 +59,7 @@ Base *ED_armature_base_and_ebone_from_select_buffer(Base **bases, const uint hit_object = select_id & 0xFFFF; Base *base = NULL; EditBone *ebone = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ + /* TODO(@campbellbarton): optimize, eg: sort & binary search. */ for (uint base_index = 0; base_index < bases_len; base_index++) { if (bases[base_index]->object->runtime.select_id == hit_object) { base = bases[base_index]; @@ -83,7 +83,7 @@ Object *ED_armature_object_and_ebone_from_select_buffer(Object **objects, const uint hit_object = select_id & 0xFFFF; Object *ob = NULL; EditBone *ebone = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ + /* TODO(@campbellbarton): optimize, eg: sort & binary search. */ for (uint ob_index = 0; ob_index < objects_len; ob_index++) { if (objects[ob_index]->runtime.select_id == hit_object) { ob = objects[ob_index]; @@ -107,7 +107,7 @@ Base *ED_armature_base_and_pchan_from_select_buffer(Base **bases, const uint hit_object = select_id & 0xFFFF; Base *base = NULL; bPoseChannel *pchan = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ + /* TODO(@campbellbarton): optimize, eg: sort & binary search. */ for (uint base_index = 0; base_index < bases_len; base_index++) { if (bases[base_index]->object->runtime.select_id == hit_object) { base = bases[base_index]; @@ -1452,7 +1452,7 @@ static void armature_select_more_less(Object *ob, bool more) bArmature *arm = (bArmature *)ob->data; EditBone *ebone; - /* XXX(campbell): eventually we shouldn't need this. */ + /* XXX(@campbellbarton): eventually we shouldn't need this. */ ED_armature_edit_sync_selection(arm->edbo); /* count bones & store selection state */ diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index b6b5d3ee495..6756dec1c95 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -166,7 +166,7 @@ bool ED_armature_pose_select_pick_bone(ViewLayer *view_layer, /* Since we do unified select, we don't shift+select a bone if the * armature object was not active yet. - * NOTE(campbell): special exception for armature mode so we can do multi-select + * NOTE(@campbellbarton): special exception for armature mode so we can do multi-select * we could check for multi-select explicitly but think its fine to * always give predictable behavior in weight paint mode. */ if ((ob_act == NULL) || ((ob_act != ob) && (ob_act->mode & OB_MODE_ALL_WEIGHT_PAINT) == 0)) { diff --git a/source/blender/editors/asset/ED_asset_list.h b/source/blender/editors/asset/ED_asset_list.h index 2dc67fc4d37..b54f81004f2 100644 --- a/source/blender/editors/asset/ED_asset_list.h +++ b/source/blender/editors/asset/ED_asset_list.h @@ -24,7 +24,7 @@ struct wmNotifier; void ED_assetlist_storage_fetch(const struct AssetLibraryReference *library_reference, const struct bContext *C); void ED_assetlist_ensure_previews_job(const struct AssetLibraryReference *library_reference, - struct bContext *C); + const struct bContext *C); void ED_assetlist_clear(const struct AssetLibraryReference *library_reference, struct bContext *C); bool ED_assetlist_storage_has_list_for_library(const AssetLibraryReference *library_reference); /** diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index 67e253a4fcd..773838a54cd 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -97,10 +97,8 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf( RNA_enum_item_add_separator(&item, &totitem); } - int i = 0; - for (bUserAssetLibrary *user_library = (bUserAssetLibrary *)U.asset_libraries.first; - user_library; - user_library = user_library->next, i++) { + int i; + LISTBASE_FOREACH_INDEX (bUserAssetLibrary *, user_library, &U.asset_libraries, i) { /* Note that the path itself isn't checked for validity here. If an invalid library path is * used, the Asset Browser can give a nice hint on what's wrong. */ const bool is_valid = (user_library->name[0] && user_library->path[0]); diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 55167c1ed2d..b0ff5c86520 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -110,7 +110,7 @@ class AssetList : NonCopyable { void setup(); void fetch(const bContext &C); - void ensurePreviewsJob(bContext *C); + void ensurePreviewsJob(const bContext *C); void clear(bContext *C); bool needsRefetch() const; @@ -212,7 +212,7 @@ void AssetList::iterate(AssetListIterFn fn) const } } -void AssetList::ensurePreviewsJob(bContext *C) +void AssetList::ensurePreviewsJob(const bContext *C) { FileList *files = filelist_; int numfiles = filelist_files_ensure(files); @@ -422,7 +422,8 @@ void ED_assetlist_storage_fetch(const AssetLibraryReference *library_reference, AssetListStorage::fetch_library(*library_reference, *C); } -void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, bContext *C) +void ED_assetlist_ensure_previews_job(const AssetLibraryReference *library_reference, + const bContext *C) { AssetList *list = AssetListStorage::lookup_list(*library_reference); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 24302aca59b..164336c4b22 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1279,7 +1279,7 @@ void ED_curve_editnurb_make(Object *obedit) if (actkey) { // XXX strcpy(G.editModeTitleExtra, "(Key) "); - /* TODO(campbell): undo_system: investigate why this was needed. */ + /* TODO(@campbellbarton): undo_system: investigate why this was needed. */ #if 0 undo_editmode_clear(); #endif @@ -1541,67 +1541,6 @@ void CURVE_OT_split(wmOperatorType *ot) /** \name Flag Utility Functions * \{ */ -static bool isNurbselUV(const Nurb *nu, uint8_t flag, int *r_u, int *r_v) -{ - /* return (u != -1): 1 row in u-direction selected. U has value between 0-pntsv - * return (v != -1): 1 column in v-direction selected. V has value between 0-pntsu - */ - BPoint *bp; - int a, b, sel; - - *r_u = *r_v = -1; - - bp = nu->bp; - for (b = 0; b < nu->pntsv; b++) { - sel = 0; - for (a = 0; a < nu->pntsu; a++, bp++) { - if (bp->f1 & flag) { - sel++; - } - } - if (sel == nu->pntsu) { - if (*r_u == -1) { - *r_u = b; - } - else { - return 0; - } - } - else if (sel > 1) { - return 0; /* because sel == 1 is still ok */ - } - } - - for (a = 0; a < nu->pntsu; a++) { - sel = 0; - bp = &nu->bp[a]; - for (b = 0; b < nu->pntsv; b++, bp += nu->pntsu) { - if (bp->f1 & flag) { - sel++; - } - } - if (sel == nu->pntsv) { - if (*r_v == -1) { - *r_v = a; - } - else { - return 0; - } - } - else if (sel > 1) { - return 0; - } - } - - if (*r_u == -1 && *r_v > -1) { - return 1; - } - if (*r_v == -1 && *r_u > -1) { - return 1; - } - return 0; -} - /* return true if U direction is selected and number of selected columns v */ static bool isNurbselU(Nurb *nu, int *v, int flag) { @@ -1976,119 +1915,201 @@ static void ed_curve_delete_selected(Object *obedit, View3D *v3d) } } -bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag) +static void select_bpoints(BPoint *bp, + const int stride, + const int count, + const bool selstatus, + const uint8_t flag, + const bool hidden) { - BPoint *bp, *bpn, *newbp; - int a, u, v, len; - bool ok = false; + for (int i = 0; i < count; i++) { + select_bpoint(bp, selstatus, flag, hidden); + bp += stride; + } +} - LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { - if (nu->pntsv == 1) { - bp = nu->bp; - a = nu->pntsu; - while (a) { - if (bp->f1 & flag) { - /* pass */ - } - else { - break; - } - bp++; - a--; - } - if (a == 0) { - ok = true; - newbp = (BPoint *)MEM_mallocN(2 * nu->pntsu * sizeof(BPoint), "extrudeNurb1"); - ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu); - bp = newbp + nu->pntsu; - ED_curve_bpcpy(editnurb, bp, nu->bp, nu->pntsu); - MEM_freeN(nu->bp); - nu->bp = newbp; - a = nu->pntsu; - while (a--) { - select_bpoint(bp, SELECT, flag, HIDDEN); - select_bpoint(newbp, DESELECT, flag, HIDDEN); - bp++; - newbp++; - } +/** + * Calculate and return fully selected legs along i dimension. + * Calculates intervals to create extrusion by duplicating existing points while copied to + * destination NURBS. For ex. for curve of 3 points indexed by 0..2 to extrude first and last + * point copy intervals would be [0, 0][0, 2][2, 2]. Representation in copy_intervals array would + * be [0, 0, 2, 2]. Returns -1 if selection is not valid. + */ +static int sel_to_copy_ints(const BPoint *bp, + const int next_j, + const int max_j, + const int next_i, + const int max_i, + const uint8_t flag, + int copy_intervals[], + int *interval_count, + bool *out_is_first_sel) +{ + const BPoint *bp_j = bp; - nu->pntsv = 2; - nu->orderv = 2; - BKE_nurb_knot_calc_v(nu); - } - } - else { - /* which row or column is selected */ + int selected_leg_count = 0; + int ins = 0; + int selected_in_prev_leg = -1; + int not_full = -1; - if (isNurbselUV(nu, flag, &u, &v)) { + bool is_first_sel = false; + bool is_last_sel = false; - /* deselect all */ - bp = nu->bp; - a = nu->pntsu * nu->pntsv; - while (a--) { - select_bpoint(bp, DESELECT, flag, HIDDEN); - bp++; - } + for (int j = 0; j < max_j; j++, bp_j += next_j) { + const BPoint *bp_j_i = bp_j; + int selected_in_curr_leg = 0; + for (int i = 0; i < max_i; i++, bp_j_i += next_i) { + if (bp_j_i->f1 & flag) { + selected_in_curr_leg++; + } + } + if (selected_in_curr_leg == max_i) { + selected_leg_count++; + if (j == 0) { + is_first_sel = true; + } + else if (j + 1 == max_j) { + is_last_sel = true; + } + } + else if (not_full == -1) { + not_full = selected_in_curr_leg; + } + /* We have partially selected leg in opposite dimension if condition is met. */ + else if (not_full != selected_in_curr_leg) { + return -1; + } + /* Extrusion area starts/ends if met. */ + if (selected_in_prev_leg != selected_in_curr_leg) { + copy_intervals[ins] = selected_in_curr_leg == max_i || j == 0 ? j : j - 1; + ins++; + selected_in_prev_leg = selected_in_curr_leg; + } + copy_intervals[ins] = j; + } + if (selected_leg_count && + /* Prevents leading and trailing unselected legs if all selected. + * Unless it is extrusion from point or curve.*/ + (selected_leg_count < max_j || max_j == 1)) { + /* Prepend unselected leg if more than one leg selected at the starting edge. + * max_j == 1 handles extrusion from point to curve and from curve to surface cases. */ + if (is_first_sel && (copy_intervals[0] < copy_intervals[1] || max_j == 1)) { + memmove(copy_intervals + 1, copy_intervals, (ins + 1) * sizeof(copy_intervals[0])); + copy_intervals[0] = 0; + ins++; + is_first_sel = false; + } + /* Append unselected leg if more than one leg selected at the end. */ + if (is_last_sel && copy_intervals[ins - 1] < copy_intervals[ins]) { + copy_intervals[ins + 1] = copy_intervals[ins]; + ins++; + } + } + *interval_count = ins; + *out_is_first_sel = ins > 1 ? is_first_sel : false; + return selected_leg_count; +} - if (ELEM(u, 0, nu->pntsv - 1)) { /* row in u-direction selected */ - ok = true; - newbp = (BPoint *)MEM_mallocN(nu->pntsu * (nu->pntsv + 1) * sizeof(BPoint), - "extrudeNurb1"); - if (u == 0) { - len = nu->pntsv * nu->pntsu; - ED_curve_bpcpy(editnurb, newbp + nu->pntsu, nu->bp, len); - ED_curve_bpcpy(editnurb, newbp, nu->bp, nu->pntsu); - bp = newbp; - } - else { - len = nu->pntsv * nu->pntsu; - ED_curve_bpcpy(editnurb, newbp, nu->bp, len); - ED_curve_bpcpy(editnurb, newbp + len, &nu->bp[len - nu->pntsu], nu->pntsu); - bp = newbp + len; - } +typedef struct NurbDim { + int pntsu; + int pntsv; +} NurbDim; - a = nu->pntsu; - while (a--) { - select_bpoint(bp, SELECT, flag, HIDDEN); - bp++; - } +static NurbDim editnurb_find_max_points_num(const EditNurb *editnurb) +{ + NurbDim ret = {0, 0}; + LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { + if (nu->pntsu > ret.pntsu) { + ret.pntsu = nu->pntsu; + } + if (nu->pntsv > ret.pntsv) { + ret.pntsv = nu->pntsv; + } + } + return ret; +} - MEM_freeN(nu->bp); - nu->bp = newbp; - nu->pntsv++; - BKE_nurb_knot_calc_v(nu); - } - else if (ELEM(v, 0, nu->pntsu - 1)) { /* column in v-direction selected */ - ok = true; - bpn = newbp = (BPoint *)MEM_mallocN((nu->pntsu + 1) * nu->pntsv * sizeof(BPoint), - "extrudeNurb1"); - bp = nu->bp; +bool ed_editnurb_extrude_flag(EditNurb *editnurb, const uint8_t flag) +{ + const NurbDim max = editnurb_find_max_points_num(editnurb); + /* One point induces at most one interval. Except single point case, it can give + 1. + * Another +1 is for first element of the first interval. */ + int *const intvls_u = MEM_malloc_arrayN(max.pntsu + 2, sizeof(int), "extrudeNurb0"); + int *const intvls_v = MEM_malloc_arrayN(max.pntsv + 2, sizeof(int), "extrudeNurb1"); + bool ok = false; - for (a = 0; a < nu->pntsv; a++) { - if (v == 0) { - *bpn = *bp; - bpn->f1 |= flag; - bpn++; - } - ED_curve_bpcpy(editnurb, bpn, bp, nu->pntsu); - bp += nu->pntsu; - bpn += nu->pntsu; - if (v == nu->pntsu - 1) { - *bpn = *(bp - 1); - bpn->f1 |= flag; - bpn++; - } - } + LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { + int intvl_cnt_u; + bool is_first_sel_u; - MEM_freeN(nu->bp); - nu->bp = newbp; - nu->pntsu++; - BKE_nurb_knot_calc_u(nu); - } - } + /* Calculate selected U legs and intervals for their extrusion. */ + const int selected_us = sel_to_copy_ints( + nu->bp, 1, nu->pntsu, nu->pntsu, nu->pntsv, flag, intvls_u, &intvl_cnt_u, &is_first_sel_u); + if (selected_us == -1) { + continue; } - } + int intvl_cnt_v; + bool is_first_sel_v; + const bool is_point = nu->pntsu == 1; + const bool is_curve = nu->pntsv == 1; + const bool extrude_every_u_point = selected_us == nu->pntsu; + if (is_point || (is_curve && !extrude_every_u_point)) { + intvls_v[0] = intvls_v[1] = 0; + intvl_cnt_v = 1; + is_first_sel_v = false; + } + else { + sel_to_copy_ints(nu->bp, + nu->pntsu, + nu->pntsv, + 1, + nu->pntsu, + flag, + intvls_v, + &intvl_cnt_v, + &is_first_sel_v); + } + + const int new_pntsu = nu->pntsu + intvl_cnt_u - 1; + const int new_pntsv = nu->pntsv + intvl_cnt_v - 1; + BPoint *const new_bp = (BPoint *)MEM_malloc_arrayN( + new_pntsu * new_pntsv, sizeof(BPoint), "extrudeNurb2"); + BPoint *new_bp_v = new_bp; + + bool selected_v = is_first_sel_v; + for (int j = 1; j <= intvl_cnt_v; j++, selected_v = !selected_v) { + BPoint *old_bp_v = nu->bp + intvls_v[j - 1] * nu->pntsu; + for (int v_j = intvls_v[j - 1]; v_j <= intvls_v[j]; + v_j++, new_bp_v += new_pntsu, old_bp_v += nu->pntsu) { + BPoint *new_bp_u_v = new_bp_v; + bool selected_u = is_first_sel_u; + for (int i = 1; i <= intvl_cnt_u; i++, selected_u = !selected_u) { + const int copy_from = intvls_u[i - 1]; + const int copy_to = intvls_u[i]; + const int copy_count = copy_to - copy_from + 1; + const bool sel_status = selected_u || selected_v ? SELECT : DESELECT; + ED_curve_bpcpy(editnurb, new_bp_u_v, old_bp_v + copy_from, copy_count); + select_bpoints(new_bp_u_v, 1, copy_count, sel_status, flag, HIDDEN); + new_bp_u_v += copy_count; + } + } + } + + MEM_freeN(nu->bp); + nu->bp = new_bp; + nu->pntsu = new_pntsu; + if (nu->pntsv == 1 && new_pntsv > 1) { + nu->orderv = 2; + } + nu->pntsv = new_pntsv; + BKE_nurb_knot_calc_u(nu); + BKE_nurb_knot_calc_v(nu); + + ok = true; + } + MEM_freeN(intvls_u); + MEM_freeN(intvls_v); return ok; } @@ -5695,23 +5716,12 @@ static int curve_extrude_exec(bContext *C, wmOperator *UNUSED(op)) Curve *cu = obedit->data; EditNurb *editnurb = cu->editnurb; bool changed = false; - bool as_curve = false; if (!ED_curve_select_check(v3d, cu->editnurb)) { continue; } - /* First test: curve? */ - if (obedit->type != OB_CURVES_LEGACY) { - LISTBASE_FOREACH (Nurb *, nu, &editnurb->nurbs) { - if ((nu->pntsv == 1) && (ED_curve_nurb_select_count(v3d, nu) < nu->pntsu)) { - as_curve = true; - break; - } - } - } - - if (obedit->type == OB_CURVES_LEGACY || as_curve) { + if (obedit->type == OB_CURVES_LEGACY) { changed = ed_editcurve_extrude(cu, editnurb, v3d); } else { diff --git a/source/blender/editors/curves/CMakeLists.txt b/source/blender/editors/curves/CMakeLists.txt index 303d2fb71dc..945bba0a77c 100644 --- a/source/blender/editors/curves/CMakeLists.txt +++ b/source/blender/editors/curves/CMakeLists.txt @@ -13,6 +13,7 @@ set(INC ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna diff --git a/source/blender/editors/geometry/CMakeLists.txt b/source/blender/editors/geometry/CMakeLists.txt index e0c440b09b4..6e28bb3e8ec 100644 --- a/source/blender/editors/geometry/CMakeLists.txt +++ b/source/blender/editors/geometry/CMakeLists.txt @@ -10,6 +10,7 @@ set(INC ../../makesrna ../../windowmanager ../../../../intern/guardedalloc + ../../bmesh ) set(INC_SYS diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 09a3cac0d48..9cb9e7ca1af 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -14,6 +14,7 @@ set(INC ../../windowmanager ../../../../intern/glew-mx ../../../../intern/guardedalloc + ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index b73b62d5a9d..a1c1c816d4c 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -144,6 +144,7 @@ void BM_uv_element_map_free(struct UvElementMap *element_map); struct UvElement *BM_uv_element_get(struct UvElementMap *map, struct BMFace *efa, struct BMLoop *l); +struct UvElement *BM_uv_element_get_head(struct UvElementMap *map, struct UvElement *child); /** * Can we edit UV's for this mesh? @@ -389,7 +390,10 @@ void ED_keymap_mesh(struct wmKeyConfig *keyconf); * Copy the face flags, most importantly selection from the mesh to the final derived mesh, * use in object mode when selecting faces (while painting). */ -void paintface_flush_flags(struct bContext *C, struct Object *ob, short flag); +void paintface_flush_flags(struct bContext *C, + struct Object *ob, + bool flush_selection, + bool flush_hidden); /** * \return True when pick finds an element or the selection changed. */ @@ -444,7 +448,7 @@ void ED_mesh_mirrtopo_init(struct BMEditMesh *em, bool skip_em_vert_array_init); void ED_mesh_mirrtopo_free(MirrTopoStore_t *mesh_topo_store); -/* object_vgroup.c */ +/* object_vgroup.cc */ #define WEIGHT_REPLACE 1 #define WEIGHT_ADD 2 @@ -552,16 +556,10 @@ void ED_mesh_uv_loop_reset_ex(struct Mesh *me, int layernum); bool ED_mesh_color_ensure(struct Mesh *me, const char *name); int ED_mesh_color_add( struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports); -bool ED_mesh_color_remove_index(struct Mesh *me, int n); -bool ED_mesh_color_remove_active(struct Mesh *me); -bool ED_mesh_color_remove_named(struct Mesh *me, const char *name); - -bool ED_mesh_sculpt_color_ensure(struct Mesh *me, const char *name); -int ED_mesh_sculpt_color_add( - struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports); -bool ED_mesh_sculpt_color_remove_index(struct Mesh *me, int n); -bool ED_mesh_sculpt_color_remove_active(struct Mesh *me); -bool ED_mesh_sculpt_color_remove_named(struct Mesh *me, const char *name); +int ED_mesh_sculpt_color_add(struct Mesh *me, + const char *name, + bool do_init, + struct ReportList *reports); void ED_mesh_report_mirror(struct wmOperator *op, int totmirr, int totfail); void ED_mesh_report_mirror_ex(struct wmOperator *op, int totmirr, int totfail, char selectmode); diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index db44d9af706..f9ca578f282 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -57,13 +57,13 @@ struct SnapObjectParams { /* Geometry for snapping in edit mode. */ eSnapEditType edit_mode_type; /* snap to the closest element, use when using more than one snap type */ - bool use_occlusion_test : true; + bool use_occlusion_test : 1; /* exclude back facing geometry from snapping */ - bool use_backface_culling : true; + bool use_backface_culling : 1; /* Break nearest face snapping into steps to improve transformations across U-shaped targets. */ short face_nearest_steps; /* Enable to force nearest face snapping to snap to target the source was initially near. */ - bool keep_on_same_target; + bool keep_on_same_target : 1; }; typedef struct SnapObjectContext SnapObjectContext; diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 80a75da27f8..24d6819536d 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -305,6 +305,29 @@ void ED_uvedit_buttons_register(struct ARegionType *art); /* uvedit_islands.c */ +struct FaceIsland { + struct FaceIsland *next; + struct FaceIsland *prev; + struct BMFace **faces; + int faces_len; + rctf bounds_rect; + /** + * \note While this is duplicate information, + * it allows islands from multiple meshes to be stored in the same list. + */ + uint cd_loop_uv_offset; + float aspect_y; +}; + +int bm_mesh_calc_uv_islands(const Scene *scene, + struct BMesh *bm, + ListBase *island_list, + const bool only_selected_faces, + const bool only_selected_uvs, + const bool use_seams, + const float aspect_y, + const uint cd_loop_uv_offset); + struct UVMapUDIM_Params { const struct Image *image; /** Copied from #SpaceImage.tile_grid_shape */ diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 7d31950c869..bb95ea97c1c 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -1171,7 +1171,7 @@ void ED_view3d_camera_lock_init(const struct Depsgraph *depsgraph, * * Apply the 3D Viewport transformation back to the camera object. * - * \return true if the camera is moved. + * \return true if the camera (or one of it's parents) was moved. */ bool ED_view3d_camera_lock_sync(const struct Depsgraph *depsgraph, struct View3D *v3d, @@ -1211,8 +1211,8 @@ bool ED_view3d_camera_lock_undo_test(const View3D *v3d, * \return true when the call to push an undo step was made. */ bool ED_view3d_camera_lock_undo_push(const char *str, - View3D *v3d, - struct RegionView3D *rv3d, + const View3D *v3d, + const struct RegionView3D *rv3d, struct bContext *C); /** @@ -1222,8 +1222,8 @@ bool ED_view3d_camera_lock_undo_push(const char *str, * where adding a separate undo step each time isn't desirable. */ bool ED_view3d_camera_lock_undo_grouped_push(const char *str, - View3D *v3d, - struct RegionView3D *rv3d, + const View3D *v3d, + const struct RegionView3D *rv3d, struct bContext *C); #define VIEW3D_MARGIN 1.4f diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index a8d25b75036..163ea7e9493 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -85,7 +85,7 @@ typedef struct uiViewItemHandle uiViewItemHandle; /* Separator for text in search menus (right pointing arrow). * keep in sync with `string_search.cc`. */ -#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" +#define UI_MENU_ARROW_SEP "\xe2\x96\xb8" /* names */ #define UI_MAX_DRAW_STR 400 @@ -2482,7 +2482,7 @@ enum uiTemplateListFlags { ENUM_OPERATORS(enum uiTemplateListFlags, UI_TEMPLATE_LIST_FLAGS_LAST); void uiTemplateList(uiLayout *layout, - struct bContext *C, + const struct bContext *C, const char *listtype_name, const char *list_id, struct PointerRNA *dataptr, @@ -2496,7 +2496,7 @@ void uiTemplateList(uiLayout *layout, int columns, enum uiTemplateListFlags flags); struct uiList *uiTemplateList_ex(uiLayout *layout, - struct bContext *C, + const struct bContext *C, const char *listtype_name, const char *list_id, struct PointerRNA *dataptr, @@ -2566,7 +2566,7 @@ enum { UI_TEMPLATE_ASSET_DRAW_NO_LIBRARY = (1 << 2), }; void uiTemplateAssetView(struct uiLayout *layout, - struct bContext *C, + const struct bContext *C, const char *list_id, struct PointerRNA *asset_library_dataptr, const char *asset_library_propname, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 07c3bfdafbf..e4a973a375e 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -21,21 +21,22 @@ set(INC ../../windowmanager ../../../../intern/glew-mx ../../../../intern/guardedalloc + ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) set(SRC - eyedroppers/interface_eyedropper.c eyedroppers/eyedropper_color.c eyedroppers/eyedropper_colorband.c eyedroppers/eyedropper_datablock.c eyedroppers/eyedropper_depth.c eyedroppers/eyedropper_driver.c eyedroppers/eyedropper_gpencil_color.c + eyedroppers/interface_eyedropper.c interface.cc interface_align.c - interface_anim.c + interface_anim.cc interface_button_group.c interface_context_menu.c interface_context_path.cc @@ -46,8 +47,8 @@ set(SRC interface_icons.c interface_icons_event.c interface_layout.c - interface_ops.c - interface_panel.c + interface_ops.cc + interface_panel.cc interface_query.cc interface_region_color_picker.cc interface_region_hud.cc @@ -56,16 +57,16 @@ set(SRC interface_region_popover.cc interface_region_popup.cc interface_region_search.cc - interface_region_tooltip.c + interface_region_tooltip.cc interface_regions.cc interface_style.cc interface_template_asset_view.cc interface_template_attribute_search.cc interface_template_list.cc interface_template_search_menu.cc - interface_template_search_operator.c + interface_template_search_operator.cc interface_templates.c - interface_undo.c + interface_undo.cc interface_utils.cc interface_widgets.c resources.c @@ -82,7 +83,7 @@ set(SRC eyedroppers/eyedropper_intern.h interface_intern.h - interface_regions_intern.h + interface_regions_intern.hh ) set(LIB diff --git a/source/blender/editors/interface/interface.cc b/source/blender/editors/interface/interface.cc index 2f9e69137ed..c076845af3c 100644 --- a/source/blender/editors/interface/interface.cc +++ b/source/blender/editors/interface/interface.cc @@ -1310,7 +1310,7 @@ static bool ui_but_event_operator_string_from_panel(const bContext *C, IDP_AddToGroup(prop_panel, IDP_New(IDP_INT, ®ion_type_val, "region_type")); for (int i = 0; i < 2; i++) { - /* FIXME(campbell): We can't reasonably search all configurations - long term. */ + /* FIXME(@campbellbarton): We can't reasonably search all configurations - long term. */ IDPropertyTemplate val = {0}; val.i = i; diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.cc index d7d1d3ce260..4da6cefd8de 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.cc @@ -4,9 +4,9 @@ * \ingroup edinterface */ -#include <stdio.h> -#include <stdlib.h> -#include <string.h> +#include <cstdio> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -49,8 +49,14 @@ static FCurve *ui_but_get_fcurve( * but works well enough in typical cases */ const int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex; - return BKE_fcurve_find_by_rna_context_ui( - but->block->evil_C, &but->rnapoin, but->rnaprop, rnaindex, adt, action, r_driven, r_special); + return BKE_fcurve_find_by_rna_context_ui(static_cast<bContext *>(but->block->evil_C), + &but->rnapoin, + but->rnaprop, + rnaindex, + adt, + action, + r_driven, + r_special); } void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context) @@ -93,7 +99,7 @@ void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context) } /* XXX: this feature is totally broken and useless with NLA */ - if (adt == NULL || adt->nla_tracks.first == NULL) { + if (adt == nullptr || adt->nla_tracks.first == nullptr) { const AnimationEvalContext remapped_context = BKE_animsys_eval_context_construct_at( anim_eval_context, cfra); if (fcurve_is_changed(but->rnapoin, but->rnaprop, fcu, &remapped_context)) { @@ -109,13 +115,13 @@ void ui_but_anim_flag(uiBut *but, const AnimationEvalContext *anim_eval_context) static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but_decorate) { - uiBut *but_iter = NULL; + uiBut *but_iter = nullptr; BLI_assert(UI_but_is_decorator(&but_decorate->but)); BLI_assert(but_decorate->rnapoin.data && but_decorate->rnaprop); LISTBASE_CIRCULAR_BACKWARD_BEGIN ( - &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev) { + uiBut *, &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev) { if (but_iter != (uiBut *)but_decorate && ui_but_rna_equals_ex( but_iter, &but_decorate->rnapoin, but_decorate->rnaprop, but_decorate->rnaindex)) { @@ -123,9 +129,9 @@ static uiBut *ui_but_anim_decorate_find_attached_button(uiButDecorator *but_deco } } LISTBASE_CIRCULAR_BACKWARD_END( - &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev); + uiBut *, &but_decorate->but.block->buttons, but_iter, but_decorate->but.prev); - return NULL; + return nullptr; } void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but) @@ -173,7 +179,7 @@ bool ui_but_anim_expression_get(uiBut *but, char *str, size_t maxlen) ChannelDriver *driver; bool driven, special; - fcu = ui_but_get_fcurve(but, NULL, NULL, &driven, &special); + fcu = ui_but_get_fcurve(but, nullptr, nullptr, &driven, &special); if (fcu && driven) { driver = fcu->driver; @@ -195,13 +201,13 @@ bool ui_but_anim_expression_set(uiBut *but, const char *str) ChannelDriver *driver; bool driven, special; - fcu = ui_but_get_fcurve(but, NULL, NULL, &driven, &special); + fcu = ui_but_get_fcurve(but, nullptr, nullptr, &driven, &special); if (fcu && driven) { driver = fcu->driver; if (driver && (driver->type == DRIVER_TYPE_PYTHON)) { - bContext *C = but->block->evil_C; + bContext *C = static_cast<bContext *>(but->block->evil_C); BLI_strncpy_utf8(driver->expression, str, sizeof(driver->expression)); @@ -213,7 +219,7 @@ bool ui_but_anim_expression_set(uiBut *but, const char *str) fcu->flag &= ~FCURVE_DISABLED; /* this notifier should update the Graph Editor and trigger depsgraph refresh? */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL); + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, nullptr); DEG_relations_tag_update(CTX_data_main(C)); @@ -226,14 +232,14 @@ bool ui_but_anim_expression_set(uiBut *but, const char *str) bool ui_but_anim_expression_create(uiBut *but, const char *str) { - bContext *C = but->block->evil_C; + bContext *C = static_cast<bContext *>(but->block->evil_C); ID *id; FCurve *fcu; char *path; bool ok = false; /* button must have RNA-pointer to a numeric-capable property */ - if (ELEM(NULL, but->rnapoin.data, but->rnaprop)) { + if (ELEM(nullptr, but->rnapoin.data, but->rnaprop)) { if (G.debug & G_DEBUG) { printf("ERROR: create expression failed - button has no RNA info attached\n"); } @@ -253,7 +259,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) /* FIXME: until materials can be handled by depsgraph, * don't allow drivers to be created for them */ id = but->rnapoin.owner_id; - if ((id == NULL) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) { + if ((id == nullptr) || (GS(id->name) == ID_MA) || (GS(id->name) == ID_TE)) { if (G.debug & G_DEBUG) { printf("ERROR: create expression failed - invalid data-block for adding drivers (%p)\n", id); } @@ -262,7 +268,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) /* get path */ path = RNA_path_from_ID_to_property(&but->rnapoin, but->rnaprop); - if (path == NULL) { + if (path == nullptr) { return false; } @@ -282,7 +288,7 @@ bool ui_but_anim_expression_create(uiBut *but, const char *str) /* updates */ BKE_driver_invalidate_expression(driver, true, false); DEG_relations_tag_update(CTX_data_main(C)); - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, NULL); + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME, nullptr); ok = true; } } @@ -300,26 +306,26 @@ void ui_but_anim_autokey(bContext *C, uiBut *but, Scene *scene, float cfra) void ui_but_anim_copy_driver(bContext *C) { /* this operator calls UI_context_active_but_prop_get */ - WM_operator_name_call(C, "ANIM_OT_copy_driver_button", WM_OP_INVOKE_DEFAULT, NULL, NULL); + WM_operator_name_call(C, "ANIM_OT_copy_driver_button", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); } void ui_but_anim_paste_driver(bContext *C) { /* this operator calls UI_context_active_but_prop_get */ - WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, NULL, NULL); + WM_operator_name_call(C, "ANIM_OT_paste_driver_button", WM_OP_INVOKE_DEFAULT, nullptr, nullptr); } void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy)) { wmWindowManager *wm = CTX_wm_manager(C); - uiButDecorator *but_decorate = arg_but; + uiButDecorator *but_decorate = static_cast<uiButDecorator *>(arg_but); uiBut *but_anim = ui_but_anim_decorate_find_attached_button(but_decorate); if (!but_anim) { return; } - /* FIXME(campbell), swapping active pointer is weak. */ + /* FIXME(@campbellbarton): swapping active pointer is weak. */ SWAP(struct uiHandleButtonData *, but_anim->active, but_decorate->but.active); wm->op_undo_depth++; @@ -332,7 +338,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy) wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_delete_button", false); WM_operator_properties_create_ptr(&props_ptr, ot); RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr); WM_operator_properties_free(&props_ptr); } else { @@ -340,7 +346,7 @@ void ui_but_anim_decorate_cb(bContext *C, void *arg_but, void *UNUSED(arg_dummy) wmOperatorType *ot = WM_operatortype_find("ANIM_OT_keyframe_insert_button", false); WM_operator_properties_create_ptr(&props_ptr, ot); RNA_boolean_set(&props_ptr, "all", but_anim->rnaindex == -1); - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, NULL); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr, nullptr); WM_operator_properties_free(&props_ptr); } diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 80fd0cbe16e..6ee421fb4d2 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -4344,15 +4344,18 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but, ARegion *region, const wmEvent *event) { - float xmax = but->rect.xmax; - const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */ - int x = event->xy[0], y = event->xy[1]; + if (BLI_listbase_is_empty(&but->extra_op_icons)) { + return NULL; + } + int x = event->xy[0], y = event->xy[1]; ui_window_to_block(region, but->block, &x, &y); if (!BLI_rctf_isect_pt(&but->rect, x, y)) { return NULL; } + const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */ + float xmax = but->rect.xmax; /* Same as in 'widget_draw_extra_icons', icon padding from the right edge. */ xmax -= 0.2 * icon_size; @@ -8810,7 +8813,7 @@ void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo) { uiBut *activebut = ui_context_rna_button_active(C); if (activebut) { - /* TODO(campbell): look into a better way to handle the button change + /* TODO(@campbellbarton): look into a better way to handle the button change * currently this is mainly so reset defaults works for the * operator redo panel. */ uiBlock *block = activebut->block; diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index c19e842aad8..5bb33576723 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -1824,7 +1824,7 @@ static void icon_draw_size(float x, } else if (di->type == ICON_TYPE_GEOM) { #ifdef USE_UI_TOOLBAR_HACK - /* TODO(campbell): scale icons up for toolbar, + /* TODO(@campbellbarton): scale icons up for toolbar, * we need a way to detect larger buttons and do this automatic. */ { float scale = (float)ICON_DEFAULT_HEIGHT_TOOLBAR / (float)ICON_DEFAULT_HEIGHT; @@ -1839,7 +1839,7 @@ static void icon_draw_size(float x, const bool geom_inverted = di->data.geom.inverted; /* This could re-generate often if rendered at different sizes in the one interface. - * TODO(campbell): support caching multiple sizes. */ + * TODO(@campbellbarton): support caching multiple sizes. */ ImBuf *ibuf = di->data.geom.image_cache; if ((ibuf == NULL) || (ibuf->x != w) || (ibuf->y != h) || (invert != geom_inverted)) { if (ibuf) { diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 03b9d03a6e3..d75d86c2665 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -468,6 +468,7 @@ typedef enum uiButtonGroupFlag { /** The buttons in this group are inside a panel header. */ UI_BUTTON_GROUP_PANEL_HEADER = (1 << 1), } uiButtonGroupFlag; +ENUM_OPERATORS(uiButtonGroupFlag, UI_BUTTON_GROUP_PANEL_HEADER); struct uiBlock { uiBlock *next, *prev; diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.cc index 4a5919864c7..2533a5454a5 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.cc @@ -5,7 +5,7 @@ * \ingroup edinterface */ -#include <string.h> +#include <cstring> #include "MEM_guardedalloc.h" @@ -128,7 +128,7 @@ static int copy_data_path_button_exec(bContext *C, wmOperator *op) /* try to create driver using property retrieved from UI */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - if (ptr.owner_id != NULL) { + if (ptr.owner_id != nullptr) { if (full_path) { if (prop) { path = RNA_path_full_property_py_ex(bmain, &ptr, prop, index, true); @@ -217,7 +217,7 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *op) if (ptr.owner_id && ptr.data && prop) { ID *id; - const int dim = RNA_property_array_dimension(&ptr, prop, NULL); + const int dim = RNA_property_array_dimension(&ptr, prop, nullptr); char *path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, dim, index, &id); if (path) { @@ -261,25 +261,25 @@ static bool copy_python_command_button_poll(bContext *C) { uiBut *but = UI_context_active_but_get(C); - if (but && (but->optype != NULL)) { - return 1; + if (but && (but->optype != nullptr)) { + return true; } - return 0; + return false; } static int copy_python_command_button_exec(bContext *C, wmOperator *UNUSED(op)) { uiBut *but = UI_context_active_but_get(C); - if (but && (but->optype != NULL)) { + if (but && (but->optype != nullptr)) { PointerRNA *opptr; char *str; opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */ - str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr); + str = WM_operator_pystring_ex(C, nullptr, false, true, but->optype, opptr); - WM_clipboard_text_set(str, 0); + WM_clipboard_text_set(str, false); MEM_freeN(str); @@ -391,7 +391,8 @@ static void UI_OT_reset_default_button(wmOperatorType *ot) ot->flag = 0; /* properties */ - RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array"); + RNA_def_boolean( + ot->srna, "all", true, "All", "Reset to default values all elements of the array"); } /** \} */ @@ -530,7 +531,7 @@ static EnumPropertyItem override_type_items[] = { 0, "Factor", "Store factor to linked data value (useful e.g. for scale)"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static bool override_type_set_button_poll(bContext *C) @@ -581,16 +582,16 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op) /* try to reset the nominated setting to its default value */ UI_context_active_but_prop_get(C, &ptr, &prop, &index); - BLI_assert(ptr.owner_id != NULL); + BLI_assert(ptr.owner_id != nullptr); if (all) { index = -1; } IDOverrideLibraryPropertyOperation *opop = RNA_property_override_property_operation_get( - CTX_data_main(C), &ptr, prop, operation, index, true, NULL, &created); + CTX_data_main(C), &ptr, prop, operation, index, true, nullptr, &created); - if (opop == NULL) { + if (opop == nullptr) { /* Sometimes e.g. RNA cannot generate a path to the given property. */ BKE_reportf(op->reports, RPT_WARNING, "Failed to create the override operation"); return OPERATOR_CANCELLED; @@ -601,7 +602,7 @@ static int override_type_set_button_exec(bContext *C, wmOperator *op) } /* Outliner e.g. has to be aware of this change. */ - WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr); return operator_button_property_finish(C, &ptr, prop); } @@ -634,7 +635,8 @@ static void UI_OT_override_type_set_button(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array"); + RNA_def_boolean( + ot->srna, "all", true, "All", "Reset to default values all elements of the array"); ot->prop = RNA_def_enum(ot->srna, "type", override_type_items, @@ -671,8 +673,8 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) ID *id = ptr.owner_id; IDOverrideLibraryProperty *oprop = RNA_property_override_property_find(bmain, &ptr, prop, &id); - BLI_assert(oprop != NULL); - BLI_assert(id != NULL && id->override_library != NULL); + BLI_assert(oprop != nullptr); + BLI_assert(id != nullptr && id->override_library != nullptr); const bool is_template = ID_IS_OVERRIDE_LIBRARY_TEMPLATE(id); @@ -691,8 +693,8 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) /* Remove override operation for given item, * add singular operations for the other items as needed. */ IDOverrideLibraryPropertyOperation *opop = BKE_lib_override_library_property_operation_find( - oprop, NULL, NULL, index, index, false, &is_strict_find); - BLI_assert(opop != NULL); + oprop, nullptr, nullptr, index, index, false, &is_strict_find); + BLI_assert(opop != nullptr); if (!is_strict_find) { /* No specific override operation, we have to get generic one, * and create item-specific override operations for all but given index, @@ -700,7 +702,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) for (int idx = RNA_property_array_length(&ptr, prop); idx--;) { if (idx != index) { BKE_lib_override_library_property_operation_get( - oprop, opop->operation, NULL, NULL, idx, idx, true, NULL, NULL); + oprop, opop->operation, nullptr, nullptr, idx, idx, true, nullptr, nullptr); } } } @@ -721,7 +723,7 @@ static int override_remove_button_exec(bContext *C, wmOperator *op) } /* Outliner e.g. has to be aware of this change. */ - WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, NULL); + WM_main_add_notifier(NC_WM | ND_LIB_OVERRIDE_CHANGED, nullptr); return operator_button_property_finish(C, &ptr, prop); } @@ -741,7 +743,8 @@ static void UI_OT_override_remove_button(wmOperatorType *ot) ot->flag = OPTYPE_UNDO; /* properties */ - RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array"); + RNA_def_boolean( + ot->srna, "all", true, "All", "Reset to default values all elements of the array"); } /** \} */ @@ -750,8 +753,8 @@ static void UI_OT_override_remove_button(wmOperatorType *ot) /** \name Copy To Selected Operator * \{ */ -#define NOT_NULL(assignment) ((assignment) != NULL) -#define NOT_RNA_NULL(assignment) ((assignment).data != NULL) +#define NOT_NULL(assignment) ((assignment) != nullptr) +#define NOT_RNA_NULL(assignment) ((assignment).data != nullptr) static void ui_context_selected_bones_via_pose(bContext *C, ListBase *r_lb) { @@ -760,7 +763,7 @@ static void ui_context_selected_bones_via_pose(bContext *C, ListBase *r_lb) if (!BLI_listbase_is_empty(&lb)) { LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { - bPoseChannel *pchan = link->ptr.data; + bPoseChannel *pchan = static_cast<bPoseChannel *>(link->ptr.data); RNA_pointer_create(link->ptr.owner_id, &RNA_Bone, pchan->bone, &link->ptr); } } @@ -776,9 +779,9 @@ bool UI_context_copy_to_selected_list(bContext *C, char **r_path) { *r_use_path_from_id = false; - *r_path = NULL; + *r_path = nullptr; /* special case for bone constraints */ - char *path_from_bone = NULL; + char *path_from_bone = nullptr; /* Remove links from the collection list which don't contain 'prop'. */ bool ensure_list_items_contain_prop = false; @@ -792,29 +795,32 @@ bool UI_context_copy_to_selected_list(bContext *C, */ if (!RNA_property_is_idprop(prop) && RNA_struct_is_a(ptr->type, &RNA_PropertyGroup)) { PointerRNA owner_ptr; - char *idpath = NULL; + char *idpath = nullptr; /* First, check the active PoseBone and PoseBone->Bone. */ if (NOT_RNA_NULL( owner_ptr = CTX_data_pointer_get_type(C, "active_pose_bone", &RNA_PoseBone))) { - if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) { + if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty( + &owner_ptr, static_cast<IDProperty *>(ptr->data)))) { *r_lb = CTX_data_collection_get(C, "selected_pose_bones"); } else { - bPoseChannel *pchan = owner_ptr.data; + bPoseChannel *pchan = static_cast<bPoseChannel *>(owner_ptr.data); RNA_pointer_create(owner_ptr.owner_id, &RNA_Bone, pchan->bone, &owner_ptr); - if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) { + if (NOT_NULL(idpath = RNA_path_from_struct_to_idproperty( + &owner_ptr, static_cast<IDProperty *>(ptr->data)))) { ui_context_selected_bones_via_pose(C, r_lb); } } } - if (idpath == NULL) { + if (idpath == nullptr) { /* Check the active EditBone if in edit mode. */ if (NOT_RNA_NULL( owner_ptr = CTX_data_pointer_get_type_silent(C, "active_bone", &RNA_EditBone)) && - NOT_NULL(idpath = RNA_path_from_struct_to_idproperty(&owner_ptr, ptr->data))) { + NOT_NULL(idpath = RNA_path_from_struct_to_idproperty( + &owner_ptr, static_cast<IDProperty *>(ptr->data)))) { *r_lb = CTX_data_collection_get(C, "selected_editable_bones"); } @@ -867,30 +873,30 @@ bool UI_context_copy_to_selected_list(bContext *C, } else if (RNA_struct_is_a(ptr->type, &RNA_Constraint) && (path_from_bone = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_PoseBone)) != - NULL) { + nullptr) { *r_lb = CTX_data_collection_get(C, "selected_pose_bones"); *r_path = path_from_bone; } else if (RNA_struct_is_a(ptr->type, &RNA_Node) || RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) { - ListBase lb = {NULL, NULL}; - char *path = NULL; - bNode *node = NULL; + ListBase lb = {nullptr, nullptr}; + char *path = nullptr; + bNode *node = nullptr; /* Get the node we're editing */ if (RNA_struct_is_a(ptr->type, &RNA_NodeSocket)) { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNodeSocket *sock = ptr->data; - if (nodeFindNode(ntree, sock, &node, NULL)) { - if ((path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node)) != NULL) { + bNodeSocket *sock = static_cast<bNodeSocket *>(ptr->data); + if (nodeFindNode(ntree, sock, &node, nullptr)) { + if ((path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Node)) != nullptr) { /* we're good! */ } else { - node = NULL; + node = nullptr; } } } else { - node = ptr->data; + node = static_cast<bNode *>(ptr->data); } /* Now filter by type */ @@ -898,7 +904,7 @@ bool UI_context_copy_to_selected_list(bContext *C, lb = CTX_data_collection_get(C, "selected_nodes"); LISTBASE_FOREACH_MUTABLE (CollectionPointerLink *, link, &lb) { - bNode *node_data = link->ptr.data; + bNode *node_data = static_cast<bNode *>(link->ptr.data); if (node_data->type != node->type) { BLI_remlink(&lb, link); @@ -929,17 +935,17 @@ bool UI_context_copy_to_selected_list(bContext *C, LISTBASE_FOREACH (CollectionPointerLink *, link, &lb) { Object *ob = (Object *)link->ptr.owner_id; if (ob->data) { - ID *id_data = ob->data; + ID *id_data = static_cast<ID *>(ob->data); id_data->tag |= LIB_TAG_DOIT; } } LISTBASE_FOREACH_MUTABLE (CollectionPointerLink *, link, &lb) { Object *ob = (Object *)link->ptr.owner_id; - ID *id_data = ob->data; + ID *id_data = static_cast<ID *>(ob->data); - if ((id_data == NULL) || (id_data->tag & LIB_TAG_DOIT) == 0 || ID_IS_LINKED(id_data) || - (GS(id_data->name) != id_code)) { + if ((id_data == nullptr) || (id_data->tag & LIB_TAG_DOIT) == 0 || + ID_IS_LINKED(id_data) || (GS(id_data->name) != id_code)) { BLI_remlink(&lb, link); MEM_freeN(link); } @@ -961,7 +967,8 @@ bool UI_context_copy_to_selected_list(bContext *C, /* Sequencer's ID is scene :/ */ /* Try to recursively find an RNA_Sequence ancestor, * to handle situations like T41062... */ - if ((*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence)) != NULL) { + if ((*r_path = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_Sequence)) != + nullptr) { /* Special case when we do this for 'Sequence.lock'. * (if the sequence is locked, it won't be in "selected_editable_sequences"). */ const char *prop_id = RNA_property_identifier(prop); @@ -975,7 +982,7 @@ bool UI_context_copy_to_selected_list(bContext *C, ensure_list_items_contain_prop = true; } } - return (*r_path != NULL); + return (*r_path != nullptr); } else { return false; @@ -1013,13 +1020,13 @@ bool UI_context_copy_to_selected_check(PointerRNA *ptr, if (use_path_from_id) { /* Path relative to ID. */ - lprop = NULL; + lprop = nullptr; RNA_id_pointer_create(ptr_link->owner_id, &idptr); RNA_path_resolve_property(&idptr, path, &lptr, &lprop); } else if (path) { /* Path relative to elements from list. */ - lprop = NULL; + lprop = nullptr; RNA_path_resolve_property(ptr_link, path, &lptr, &lprop); } else { @@ -1034,7 +1041,7 @@ bool UI_context_copy_to_selected_check(PointerRNA *ptr, /* Skip non-existing properties on link. This was previously covered with the `lprop != prop` * check but we are now more permissive when it comes to ID properties, see below. */ - if (lprop == NULL) { + if (lprop == nullptr) { return false; } @@ -1105,13 +1112,13 @@ static bool copy_to_selected_button(bContext *C, bool all, bool poll) UI_context_active_but_prop_get(C, &ptr, &prop, &index); /* if there is a valid property that is editable... */ - if (ptr.data == NULL || prop == NULL) { + if (ptr.data == nullptr || prop == nullptr) { return false; } - char *path = NULL; + char *path = nullptr; bool use_path_from_id; - ListBase lb = {NULL}; + ListBase lb = {nullptr}; if (!UI_context_copy_to_selected_list(C, &ptr, prop, &lb, &use_path_from_id, &path)) { return false; @@ -1198,7 +1205,7 @@ static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll) /* Verify pointer type. */ char bone_name[MAXBONENAME]; - const StructRNA *target_type = NULL; + const StructRNA *target_type = nullptr; if (ELEM(ptr.type, &RNA_EditBone, &RNA_PoseBone, &RNA_Bone)) { RNA_string_get(&ptr, "name", bone_name); @@ -1210,13 +1217,13 @@ static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll) target_type = &RNA_Object; } - if (target_type == NULL) { + if (target_type == nullptr) { return false; } /* Find the containing Object. */ ViewLayer *view_layer = CTX_data_view_layer(C); - Base *base = NULL; + Base *base = nullptr; const short id_type = GS(ptr.owner_id->name); if (id_type == ID_OB) { base = BKE_view_layer_base_find(view_layer, (Object *)ptr.owner_id); @@ -1226,7 +1233,7 @@ static bool jump_to_target_ptr(bContext *C, PointerRNA ptr, const bool poll) } bool ok = false; - if ((base == NULL) || ((target_type == &RNA_Bone) && (base->object->type != OB_ARMATURE))) { + if ((base == nullptr) || ((target_type == &RNA_Bone) && (base->object->type != OB_ARMATURE))) { /* pass */ } else if (poll) { @@ -1278,13 +1285,14 @@ static bool jump_to_target_button(bContext *C, bool poll) if (type == PROP_STRING) { const uiBut *but = UI_context_active_but_get(C); const uiButSearch *search_but = (but->type == UI_BTYPE_SEARCH_MENU) ? (uiButSearch *)but : - NULL; + nullptr; if (search_but && search_but->items_update_fn == ui_rna_collection_search_update_fn) { - uiRNACollectionSearch *coll_search = search_but->arg; + uiRNACollectionSearch *coll_search = static_cast<uiRNACollectionSearch *>(search_but->arg); char str_buf[MAXBONENAME]; - char *str_ptr = RNA_property_string_get_alloc(&ptr, prop, str_buf, sizeof(str_buf), NULL); + char *str_ptr = RNA_property_string_get_alloc( + &ptr, prop, str_buf, sizeof(str_buf), nullptr); int found = RNA_property_collection_lookup_string( &coll_search->search_ptr, coll_search->search_prop, str_ptr, &target_ptr); @@ -1342,39 +1350,39 @@ static void UI_OT_jump_to_target_button(wmOperatorType *ot) /* EditSource Utility funcs and operator, * NOTE: this includes utility functions and button matching checks. */ -typedef struct uiEditSourceStore { +struct uiEditSourceStore { uiBut but_orig; GHash *hash; -} uiEditSourceStore; +}; -typedef struct uiEditSourceButStore { +struct uiEditSourceButStore { char py_dbg_fn[FILE_MAX]; int py_dbg_line_number; -} uiEditSourceButStore; +}; /* should only ever be set while the edit source operator is running */ -static struct uiEditSourceStore *ui_editsource_info = NULL; +static uiEditSourceStore *ui_editsource_info = nullptr; bool UI_editsource_enable_check(void) { - return (ui_editsource_info != NULL); + return (ui_editsource_info != nullptr); } static void ui_editsource_active_but_set(uiBut *but) { - BLI_assert(ui_editsource_info == NULL); + BLI_assert(ui_editsource_info == nullptr); - ui_editsource_info = MEM_callocN(sizeof(uiEditSourceStore), __func__); + ui_editsource_info = MEM_cnew<uiEditSourceStore>(__func__); memcpy(&ui_editsource_info->but_orig, but, sizeof(uiBut)); ui_editsource_info->hash = BLI_ghash_ptr_new(__func__); } -static void ui_editsource_active_but_clear(void) +static void ui_editsource_active_but_clear() { - BLI_ghash_free(ui_editsource_info->hash, NULL, MEM_freeN); + BLI_ghash_free(ui_editsource_info->hash, nullptr, MEM_freeN); MEM_freeN(ui_editsource_info); - ui_editsource_info = NULL; + ui_editsource_info = nullptr; } static bool ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b) @@ -1395,11 +1403,14 @@ static bool ui_editsource_uibut_match(uiBut *but_a, uiBut *but_b) return false; } +extern "C" { +void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno); +} + void UI_editsource_active_but_test(uiBut *but) { - extern void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno); - struct uiEditSourceButStore *but_store = MEM_callocN(sizeof(uiEditSourceButStore), __func__); + uiEditSourceButStore *but_store = MEM_cnew<uiEditSourceButStore>(__func__); const char *fn; int line_number = -1; @@ -1424,9 +1435,10 @@ void UI_editsource_active_but_test(uiBut *but) void UI_editsource_but_replace(const uiBut *old_but, uiBut *new_but) { - uiEditSourceButStore *but_store = BLI_ghash_lookup(ui_editsource_info->hash, old_but); + uiEditSourceButStore *but_store = static_cast<uiEditSourceButStore *>( + BLI_ghash_lookup(ui_editsource_info->hash, old_but)); if (but_store) { - BLI_ghash_remove(ui_editsource_info->hash, old_but, NULL, NULL); + BLI_ghash_remove(ui_editsource_info->hash, old_but, nullptr, nullptr); BLI_ghash_insert(ui_editsource_info->hash, new_but, but_store); } } @@ -1436,8 +1448,8 @@ static int editsource_text_edit(bContext *C, const char filepath[FILE_MAX], const int line) { - struct Main *bmain = CTX_data_main(C); - Text *text = NULL; + Main *bmain = CTX_data_main(C); + Text *text = nullptr; /* Developers may wish to copy-paste to an external editor. */ printf("%s:%d\n", filepath, line); @@ -1449,11 +1461,11 @@ static int editsource_text_edit(bContext *C, } } - if (text == NULL) { + if (text == nullptr) { text = BKE_text_load(bmain, filepath, BKE_main_blendfile_path(bmain)); } - if (text == NULL) { + if (text == nullptr) { BKE_reportf(op->reports, RPT_WARNING, "File '%s' cannot be opened", filepath); return OPERATOR_CANCELLED; } @@ -1477,7 +1489,7 @@ static int editsource_exec(bContext *C, wmOperator *op) if (but) { GHashIterator ghi; - struct uiEditSourceButStore *but_store = NULL; + uiEditSourceButStore *but_store = nullptr; ARegion *region = CTX_wm_region(C); int ret; @@ -1496,9 +1508,9 @@ static int editsource_exec(bContext *C, wmOperator *op) for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash); BLI_ghashIterator_done(&ghi) == false; BLI_ghashIterator_step(&ghi)) { - uiBut *but_key = BLI_ghashIterator_getKey(&ghi); + uiBut *but_key = static_cast<uiBut *>(BLI_ghashIterator_getKey(&ghi)); if (but_key && ui_editsource_uibut_match(&ui_editsource_info->but_orig, but_key)) { - but_store = BLI_ghashIterator_getValue(&ghi); + but_store = static_cast<uiEditSourceButStore *>(BLI_ghashIterator_getValue(&ghi)); break; } } @@ -1569,7 +1581,7 @@ static void edittranslation_find_po_file(const char *root, /* Now try without the second iso code part (_ES in es_ES). */ { - const char *tc = NULL; + const char *tc = nullptr; size_t szt = 0; tstr[0] = '\0'; @@ -1603,7 +1615,7 @@ static void edittranslation_find_po_file(const char *root, static int edittranslation_exec(bContext *C, wmOperator *op) { uiBut *but = UI_context_active_but_get(C); - if (but == NULL) { + if (but == nullptr) { BKE_report(op->reports, RPT_ERROR, "Active button not found"); return OPERATOR_CANCELLED; } @@ -1614,16 +1626,16 @@ static int edittranslation_exec(bContext *C, wmOperator *op) const char *root = U.i18ndir; const char *uilng = BLT_lang_get(); - uiStringInfo but_label = {BUT_GET_LABEL, NULL}; - uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL}; - uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; - uiStringInfo but_tip = {BUT_GET_TIP, NULL}; - uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL}; - uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; - uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; - uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; - uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL}; - uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL}; + uiStringInfo but_label = {BUT_GET_LABEL, nullptr}; + uiStringInfo rna_label = {BUT_GET_RNA_LABEL, nullptr}; + uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, nullptr}; + uiStringInfo but_tip = {BUT_GET_TIP, nullptr}; + uiStringInfo rna_tip = {BUT_GET_RNA_TIP, nullptr}; + uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, nullptr}; + uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, nullptr}; + uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, nullptr}; + uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, nullptr}; + uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, nullptr}; if (!BLI_is_dir(root)) { BKE_report(op->reports, @@ -1632,8 +1644,8 @@ static int edittranslation_exec(bContext *C, wmOperator *op) "Directory' path to a valid directory"); return OPERATOR_CANCELLED; } - ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, 0); - if (ot == NULL) { + ot = WM_operatortype_find(EDTSRC_I18N_OP_NAME, false); + if (ot == nullptr) { BKE_reportf(op->reports, RPT_ERROR, "Could not find operator '%s'! Please enable ui_translate add-on " @@ -1662,7 +1674,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) &rna_prop, &rna_enum, &rna_ctxt, - NULL); + nullptr); WM_operator_properties_create_ptr(&ptr, ot); RNA_string_set(&ptr, "lang", uilng); @@ -1677,7 +1689,7 @@ static int edittranslation_exec(bContext *C, wmOperator *op) RNA_string_set(&ptr, "rna_prop", rna_prop.strinfo); RNA_string_set(&ptr, "rna_enum", rna_enum.strinfo); RNA_string_set(&ptr, "rna_ctxt", rna_ctxt.strinfo); - const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, NULL); + const int ret = WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr); /* Clean up */ if (but_label.strinfo) { @@ -1737,7 +1749,7 @@ static int reloadtranslation_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) { BLT_lang_init(); BLF_cache_clear(); - BLT_lang_set(NULL); + BLT_lang_set(nullptr); UI_reinit_font(); return OPERATOR_FINISHED; } @@ -1764,13 +1776,13 @@ static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *ev bScreen *screen = CTX_wm_screen(C); const bool skip_depressed = RNA_boolean_get(op->ptr, "skip_depressed"); ARegion *region_prev = CTX_wm_region(C); - ARegion *region = screen ? BKE_screen_find_region_xy(screen, RGN_TYPE_ANY, event->xy) : NULL; + ARegion *region = screen ? BKE_screen_find_region_xy(screen, RGN_TYPE_ANY, event->xy) : nullptr; - if (region == NULL) { + if (region == nullptr) { region = region_prev; } - if (region == NULL) { + if (region == nullptr) { return OPERATOR_PASS_THROUGH; } @@ -1778,7 +1790,7 @@ static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *ev uiBut *but = UI_context_active_but_get(C); CTX_wm_region_set(C, region_prev); - if (but == NULL) { + if (but == nullptr) { return OPERATOR_PASS_THROUGH; } if (skip_depressed && (but->flag & (UI_SELECT | UI_SELECT_DRAW))) { @@ -1791,7 +1803,7 @@ static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *ev UI_but_execute(C, region, but); - but->optype = but_optype; + but->optype = static_cast<wmOperatorType *>(but_optype); WM_event_add_mousemove(CTX_wm_window(C)); @@ -1807,7 +1819,7 @@ static void UI_OT_button_execute(wmOperatorType *ot) ot->invoke = ui_button_press_invoke; ot->flag = OPTYPE_INTERNAL; - RNA_def_boolean(ot->srna, "skip_depressed", 0, "Skip Depressed", ""); + RNA_def_boolean(ot->srna, "skip_depressed", false, "Skip Depressed", ""); } /** \} */ @@ -1853,21 +1865,21 @@ bool UI_drop_color_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED( ARegion *region = CTX_wm_region(C); if (UI_but_active_drop_color(C)) { - return 1; + return true; } if (sima && (sima->mode == SI_MODE_PAINT) && sima->image && (region && region->regiontype == RGN_TYPE_WINDOW)) { - return 1; + return true; } } - return 0; + return false; } void UI_drop_color_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) { - uiDragColorHandle *drag_info = drag->poin; + uiDragColorHandle *drag_info = static_cast<uiDragColorHandle *>(drag->poin); RNA_float_set_array(drop->ptr, "color", drag_info->color); RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected); @@ -1876,7 +1888,7 @@ void UI_drop_color_copy(bContext *UNUSED(C), wmDrag *drag, wmDropBox *drop) static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); - uiBut *but = NULL; + uiBut *but = nullptr; float color[4]; bool gamma; @@ -1933,8 +1945,10 @@ static void UI_OT_drop_color(wmOperatorType *ot) ot->invoke = drop_color_invoke; ot->flag = OPTYPE_INTERNAL; - RNA_def_float_color(ot->srna, "color", 3, NULL, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0); - RNA_def_boolean(ot->srna, "gamma", 0, "Gamma Corrected", "The source color is gamma corrected"); + RNA_def_float_color( + ot->srna, "color", 3, nullptr, 0.0, FLT_MAX, "Color", "Source color", 0.0, 1.0); + RNA_def_boolean( + ot->srna, "gamma", false, "Gamma Corrected", "The source color is gamma corrected"); } /** \} */ @@ -1964,7 +1978,7 @@ static bool drop_name_poll(bContext *C) static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { uiBut *but = UI_but_active_drop_name_button(C); - char *str = RNA_string_get_alloc(op->ptr, "string", NULL, 0, NULL); + char *str = RNA_string_get_alloc(op->ptr, "string", nullptr, 0, nullptr); if (str) { ui_but_set_string_interactive(C, but, str); @@ -1985,7 +1999,7 @@ static void UI_OT_drop_name(wmOperatorType *ot) ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; RNA_def_string( - ot->srna, "string", NULL, 0, "String", "The string value to drop into the button"); + ot->srna, "string", nullptr, 0, "String", "The string value to drop into the button"); } /** \} */ @@ -2003,7 +2017,7 @@ static bool ui_list_focused_poll(bContext *C) const wmWindow *win = CTX_wm_window(C); const uiList *list = UI_list_find_mouse_over(region, win->eventstate); - return list != NULL; + return list != nullptr; } /** @@ -2026,7 +2040,7 @@ static int ui_list_start_filter_invoke(bContext *C, wmOperator *UNUSED(op), cons ARegion *region = CTX_wm_region(C); uiList *list = UI_list_find_mouse_over(region, event); /* Poll should check. */ - BLI_assert(list != NULL); + BLI_assert(list != nullptr); if (ui_list_unhide_filter_options(list)) { ui_region_redraw_immediately(C, region); @@ -2061,7 +2075,7 @@ static bool ui_view_drop_poll(bContext *C) const ARegion *region = CTX_wm_region(C); const uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, win->eventstate->xy); - return hovered_item != NULL; + return hovered_item != nullptr; } static int ui_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) @@ -2073,7 +2087,8 @@ static int ui_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven const ARegion *region = CTX_wm_region(C); uiViewItemHandle *hovered_item = UI_region_views_find_item_at(region, event->xy); - if (!UI_view_item_drop_handle(C, hovered_item, event->customdata)) { + if (!UI_view_item_drop_handle( + C, hovered_item, static_cast<const ListBase *>(event->customdata))) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } @@ -2107,7 +2122,7 @@ static bool ui_view_item_rename_poll(bContext *C) { const ARegion *region = CTX_wm_region(C); const uiViewItemHandle *active_item = UI_region_views_find_active_item(region); - return active_item != NULL && UI_view_item_can_rename(active_item); + return active_item != nullptr && UI_view_item_can_rename(active_item); } static int ui_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) @@ -2144,8 +2159,8 @@ static void UI_OT_view_item_rename(wmOperatorType *ot) static bool ui_drop_material_poll(bContext *C) { PointerRNA ptr = CTX_data_pointer_get_type(C, "object", &RNA_Object); - Object *ob = ptr.data; - if (ob == NULL) { + const Object *ob = static_cast<const Object *>(ptr.data); + if (ob == nullptr) { return false; } @@ -2163,12 +2178,12 @@ static int ui_drop_material_exec(bContext *C, wmOperator *op) Material *ma = (Material *)WM_operator_properties_id_lookup_from_name_or_session_uuid( bmain, op->ptr, ID_MA); - if (ma == NULL) { + if (ma == nullptr) { return OPERATOR_CANCELLED; } PointerRNA ptr = CTX_data_pointer_get_type(C, "object", &RNA_Object); - Object *ob = ptr.data; + Object *ob = static_cast<Object *>(ptr.data); BLI_assert(ob); PointerRNA mat_slot = CTX_data_pointer_get_type(C, "material_slot", &RNA_MaterialSlot); @@ -2176,14 +2191,14 @@ static int ui_drop_material_exec(bContext *C, wmOperator *op) const int target_slot = RNA_int_get(&mat_slot, "slot_index") + 1; /* only drop grease pencil material on grease pencil objects */ - if ((ma->gp_style != NULL) && (ob->type != OB_GPENCIL)) { + if ((ma->gp_style != nullptr) && (ob->type != OB_GPENCIL)) { return OPERATOR_CANCELLED; } BKE_object_material_assign(bmain, ob, ma, target_slot, BKE_MAT_ASSIGN_USERPREF); WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, nullptr); WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_LINKS, ma); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.cc index d4a9a4ca4cd..dc6a0fecb73 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.cc @@ -8,10 +8,10 @@ /* a full doc with API notes can be found in * bf-blender/trunk/blender/doc/guides/interface_API.txt */ -#include <ctype.h> -#include <math.h> -#include <stdlib.h> -#include <string.h> +#include <cctype> +#include <cmath> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -57,7 +57,7 @@ #define ANIMATION_TIME 0.30 #define ANIMATION_INTERVAL 0.02 -typedef enum uiPanelRuntimeFlag { +enum uiPanelRuntimeFlag { PANEL_LAST_ADDED = (1 << 0), PANEL_ACTIVE = (1 << 2), PANEL_WAS_ACTIVE = (1 << 3), @@ -78,22 +78,22 @@ typedef enum uiPanelRuntimeFlag { PANEL_IS_DRAG_DROP = (1 << 10), /** Draw a border with the active color around the panel. */ PANEL_ACTIVE_BORDER = (1 << 11), -} uiPanelRuntimeFlag; +}; /* The state of the mouse position relative to the panel. */ -typedef enum uiPanelMouseState { +enum uiPanelMouseState { PANEL_MOUSE_OUTSIDE, /** Mouse is not in the panel. */ PANEL_MOUSE_INSIDE_CONTENT, /** Mouse is in the actual panel content. */ PANEL_MOUSE_INSIDE_HEADER, /** Mouse is in the panel header. */ -} uiPanelMouseState; +}; -typedef enum uiHandlePanelState { +enum uiHandlePanelState { PANEL_STATE_DRAG, PANEL_STATE_ANIMATION, PANEL_STATE_EXIT, -} uiHandlePanelState; +}; -typedef struct uiHandlePanelData { +struct uiHandlePanelData { uiHandlePanelState state; /* Animation. */ @@ -104,17 +104,17 @@ typedef struct uiHandlePanelData { int startx, starty; int startofsx, startofsy; float start_cur_xmin, start_cur_ymin; -} uiHandlePanelData; +}; -typedef struct PanelSort { +struct PanelSort { Panel *panel; int new_offset_x; int new_offset_y; -} PanelSort; +}; static void panel_set_expansion_from_list_data(const bContext *C, Panel *panel); static int get_panel_real_size_y(const Panel *panel); -static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelState state); +static void panel_activate_state(const bContext *C, Panel *panel, const uiHandlePanelState state); static int compare_panel(const void *a, const void *b); static bool panel_type_context_poll(ARegion *region, const PanelType *panel_type, @@ -155,7 +155,7 @@ static bool panel_active_animation_changed(ListBase *lb, /* Detect animation. */ if (panel->activedata) { - uiHandlePanelData *data = panel->activedata; + uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata); if (data->state == PANEL_STATE_ANIMATION) { *r_panel_animation = panel; } @@ -178,7 +178,7 @@ static bool panel_active_animation_changed(ListBase *lb, static bool properties_space_needs_realign(const ScrArea *area, const ARegion *region) { if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) { - SpaceProperties *sbuts = area->spacedata.first; + const SpaceProperties *sbuts = static_cast<SpaceProperties *>(area->spacedata.first); if (sbuts->mainbo != sbuts->mainb) { return true; @@ -190,14 +190,14 @@ static bool properties_space_needs_realign(const ScrArea *area, const ARegion *r static bool panels_need_realign(const ScrArea *area, ARegion *region, Panel **r_panel_animation) { - *r_panel_animation = NULL; + *r_panel_animation = nullptr; if (properties_space_needs_realign(area, region)) { return true; } /* Detect if a panel was added or removed. */ - Panel *panel_animation = NULL; + Panel *panel_animation = nullptr; bool no_animation = false; if (panel_active_animation_changed(®ion->panels, &panel_animation, &no_animation)) { return true; @@ -225,7 +225,7 @@ static Panel *panel_add_instanced(ARegion *region, PanelType *panel_type, PointerRNA *custom_data) { - Panel *panel = MEM_callocN(sizeof(Panel), __func__); + Panel *panel = MEM_cnew<Panel>(__func__); panel->type = panel_type; BLI_strncpy(panel->panelname, panel_type->idname, sizeof(panel->panelname)); @@ -235,7 +235,7 @@ static Panel *panel_add_instanced(ARegion *region, /* Add the panel's children too. Although they aren't instanced panels, we can still use this * function to create them, as UI_panel_begin does other things we don't need to do. */ LISTBASE_FOREACH (LinkData *, child, &panel_type->children) { - PanelType *child_type = child->data; + PanelType *child_type = static_cast<PanelType *>(child->data); panel_add_instanced(region, &panel->children, child_type, custom_data); } @@ -265,12 +265,12 @@ Panel *UI_panel_add_instanced(const bContext *C, { ARegionType *region_type = region->type; - PanelType *panel_type = BLI_findstring( - ®ion_type->paneltypes, panel_idname, offsetof(PanelType, idname)); + PanelType *panel_type = static_cast<PanelType *>( + BLI_findstring(®ion_type->paneltypes, panel_idname, offsetof(PanelType, idname))); - if (panel_type == NULL) { + if (panel_type == nullptr) { printf("Panel type '%s' not found.\n", panel_idname); - return NULL; + return nullptr; } Panel *new_panel = panel_add_instanced(region, panels, panel_type, custom_data); @@ -314,14 +314,14 @@ void UI_panels_free_instanced(const bContext *C, ARegion *region) { /* Delete panels with the instanced flag. */ LISTBASE_FOREACH_MUTABLE (Panel *, panel, ®ion->panels) { - if ((panel->type != NULL) && (panel->type->flag & PANEL_TYPE_INSTANCED)) { + if ((panel->type != nullptr) && (panel->type->flag & PANEL_TYPE_INSTANCED)) { /* Make sure the panel's handler is removed before deleting it. */ - if (C != NULL && panel->activedata != NULL) { + if (C != nullptr && panel->activedata != nullptr) { panel_activate_state(C, panel, PANEL_STATE_EXIT); } /* Free panel's custom data. */ - if (panel->runtime.custom_data_ptr != NULL) { + if (panel->runtime.custom_data_ptr != nullptr) { MEM_freeN(panel->runtime.custom_data_ptr); } @@ -335,28 +335,28 @@ bool UI_panel_list_matches_data(ARegion *region, ListBase *data, uiListPanelIDFromDataFunc panel_idname_func) { - /* Check for NULL data. */ + /* Check for nullptr data. */ int data_len = 0; - Link *data_link = NULL; - if (data == NULL) { + Link *data_link = nullptr; + if (data == nullptr) { data_len = 0; - data_link = NULL; + data_link = nullptr; } else { data_len = BLI_listbase_count(data); - data_link = data->first; + data_link = static_cast<Link *>(data->first); } int i = 0; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { - if (panel->type != NULL && panel->type->flag & PANEL_TYPE_INSTANCED) { + if (panel->type != nullptr && panel->type->flag & PANEL_TYPE_INSTANCED) { /* The panels were reordered by drag and drop. */ if (panel->flag & PNL_INSTANCED_LIST_ORDER_CHANGED) { return false; } /* We reached the last data item before the last instanced panel. */ - if (data_link == NULL) { + if (data_link == nullptr) { return false; } @@ -383,15 +383,15 @@ bool UI_panel_list_matches_data(ARegion *region, static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *drag_panel) { /* Without a type we cannot access the reorder callback. */ - if (drag_panel->type == NULL) { + if (drag_panel->type == nullptr) { return; } /* Don't reorder if this instanced panel doesn't support drag and drop reordering. */ - if (drag_panel->type->reorder == NULL) { + if (drag_panel->type->reorder == nullptr) { return; } - char *context = NULL; + char *context = nullptr; if (!UI_panel_category_is_visible(region)) { context = drag_panel->type->context; } @@ -415,7 +415,8 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr BLI_assert(start_index != -1); /* The drag panel should definitely be in the list. */ /* Sort the matching instanced panels by their display order. */ - PanelSort *panel_sort = MEM_callocN(list_panels_len * sizeof(*panel_sort), __func__); + PanelSort *panel_sort = static_cast<PanelSort *>( + MEM_callocN(list_panels_len * sizeof(*panel_sort), __func__)); PanelSort *sort_index = panel_sort; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->type) { @@ -452,7 +453,7 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr /* Finally, move this panel's list item to the new index in its list. */ drag_panel->type->reorder(C, drag_panel, move_to_index); - CTX_store_set(C, NULL); + CTX_store_set(C, nullptr); } /** @@ -480,9 +481,9 @@ static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, */ static void panel_set_expansion_from_list_data(const bContext *C, Panel *panel) { - BLI_assert(panel->type != NULL); + BLI_assert(panel->type != nullptr); BLI_assert(panel->type->flag & PANEL_TYPE_INSTANCED); - if (panel->type->get_list_data_expand_flag == NULL) { + if (panel->type->get_list_data_expand_flag == nullptr) { /* Instanced panel doesn't support loading expansion. */ return; } @@ -504,7 +505,7 @@ static void region_panels_set_expansion_from_list_data(const bContext *C, ARegio LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PANEL_ACTIVE) { PanelType *panel_type = panel->type; - if (panel_type != NULL && panel->type->flag & PANEL_TYPE_INSTANCED) { + if (panel_type != nullptr && panel->type->flag & PANEL_TYPE_INSTANCED) { panel_set_expansion_from_list_data(C, panel); } } @@ -537,7 +538,7 @@ static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *r { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { PanelType *panel_type = panel->type; - if (panel_type == NULL) { + if (panel_type == nullptr) { continue; } @@ -563,11 +564,11 @@ static bool panel_custom_data_active_get(const Panel *panel) { /* The caller should make sure the panel is active and has a type. */ BLI_assert(UI_panel_is_active(panel)); - BLI_assert(panel->type != NULL); + BLI_assert(panel->type != nullptr); if (panel->type->active_property[0] != '\0') { PointerRNA *ptr = UI_panel_custom_data_get(panel); - if (ptr != NULL && !RNA_pointer_is_null(ptr)) { + if (ptr != nullptr && !RNA_pointer_is_null(ptr)) { return RNA_boolean_get(ptr, panel->type->active_property); } } @@ -579,12 +580,12 @@ static void panel_custom_data_active_set(Panel *panel) { /* Since the panel is interacted with, it should be active and have a type. */ BLI_assert(UI_panel_is_active(panel)); - BLI_assert(panel->type != NULL); + BLI_assert(panel->type != nullptr); if (panel->type->active_property[0] != '\0') { PointerRNA *ptr = UI_panel_custom_data_get(panel); - BLI_assert(RNA_struct_find_property(ptr, panel->type->active_property) != NULL); - if (ptr != NULL && !RNA_pointer_is_null(ptr)) { + BLI_assert(RNA_struct_find_property(ptr, panel->type->active_property) != nullptr); + if (ptr != nullptr && !RNA_pointer_is_null(ptr)) { RNA_boolean_set(ptr, panel->type->active_property, true); } } @@ -617,7 +618,7 @@ static void panel_set_runtime_flag_recursive(Panel *panel, short flag, bool valu static void panels_collapse_all(ARegion *region, const Panel *from_panel) { const bool has_category_tabs = UI_panel_category_is_visible(region); - const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : NULL; + const char *category = has_category_tabs ? UI_panel_category_active_get(region, false) : nullptr; const PanelType *from_pt = from_panel->type; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { @@ -659,7 +660,7 @@ Panel *UI_panel_find_by_type(ListBase *lb, const PanelType *pt) return panel; } } - return NULL; + return nullptr; } Panel *UI_panel_begin( @@ -668,10 +669,10 @@ Panel *UI_panel_begin( Panel *panel_last; const char *drawname = CTX_IFACE_(pt->translation_context, pt->label); const char *idname = pt->idname; - const bool newpanel = (panel == NULL); + const bool newpanel = (panel == nullptr); if (newpanel) { - panel = MEM_callocN(sizeof(Panel), __func__); + panel = MEM_cnew<Panel>(__func__); panel->type = pt; BLI_strncpy(panel->panelname, idname, sizeof(panel->panelname)); @@ -701,7 +702,7 @@ Panel *UI_panel_begin( /* If a new panel is added, we insert it right after the panel that was last added. * This way new panels are inserted in the right place between versions. */ - for (panel_last = lb->first; panel_last; panel_last = panel_last->next) { + for (panel_last = static_cast<Panel *>(lb->first); panel_last; panel_last = panel_last->next) { if (panel_last->runtime_flag & PANEL_LAST_ADDED) { BLI_remlink(lb, panel); BLI_insertlinkafter(lb, panel_last, panel); @@ -755,7 +756,7 @@ void UI_panel_header_buttons_end(Panel *panel) /* A button group should always be created in #UI_panel_header_buttons_begin. */ BLI_assert(!BLI_listbase_is_empty(&block->button_groups)); - uiButtonGroup *button_group = block->button_groups.last; + uiButtonGroup *button_group = static_cast<uiButtonGroup *>(block->button_groups.last); button_group->flag &= ~UI_BUTTON_GROUP_LOCK; @@ -770,7 +771,7 @@ void UI_panel_header_buttons_end(Panel *panel) /* Always add a new button group. Although this may result in many empty groups, without it, * new buttons in the panel body not protected with a #ui_block_new_button_group call would * end up in the panel header group. */ - ui_block_new_button_group(block, 0); + ui_block_new_button_group(block, (uiButtonGroupFlag)0); } } @@ -867,7 +868,7 @@ void ui_panel_tag_search_filter_match(Panel *panel) static void panel_matches_search_filter_recursive(const Panel *panel, bool *filter_matches) { - *filter_matches |= panel->runtime_flag & PANEL_SEARCH_FILTER_MATCH; + *filter_matches |= bool(panel->runtime_flag & PANEL_SEARCH_FILTER_MATCH); /* If the panel has no match we need to make sure that its children are too. */ if (!*filter_matches) { @@ -893,7 +894,7 @@ static void panel_set_expansion_from_search_filter_recursive(const bContext *C, { /* This has to run on inactive panels that may not have a type, * but we can prevent running on header-less panels in some cases. */ - if (panel->type == NULL || !(panel->type->flag & PANEL_TYPE_NO_HEADER)) { + if (panel->type == nullptr || !(panel->type->flag & PANEL_TYPE_NO_HEADER)) { SET_FLAG_FROM_TEST(panel->runtime_flag, use_search_closed, PANEL_USE_CLOSED_FROM_SEARCH); } @@ -926,9 +927,9 @@ static void region_panels_set_expansion_from_search_filter(const bContext *C, static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel *parent_panel) { uiBlock *block = panel->runtime.block; - BLI_assert(block != NULL); + BLI_assert(block != nullptr); BLI_assert(block->active); - if (parent_panel != NULL && UI_panel_is_closed(parent_panel)) { + if (parent_panel != nullptr && UI_panel_is_closed(parent_panel)) { /* The parent panel is closed, so this panel can be completely removed. */ UI_block_set_search_only(block, true); LISTBASE_FOREACH (uiBut *, but, &block->buttons) { @@ -944,7 +945,7 @@ static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel * continue; } LISTBASE_FOREACH (LinkData *, link, &button_group->buttons) { - uiBut *but = link->data; + uiBut *but = static_cast<uiBut *>(link->data); but->flag |= UI_HIDDEN; } } @@ -952,7 +953,7 @@ static void panel_remove_invisible_layouts_recursive(Panel *panel, const Panel * LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { if (child_panel->runtime_flag & PANEL_ACTIVE) { - BLI_assert(child_panel->runtime.block != NULL); + BLI_assert(child_panel->runtime.block != nullptr); panel_remove_invisible_layouts_recursive(child_panel, panel); } } @@ -962,8 +963,8 @@ static void region_panels_remove_invisible_layouts(ARegion *region) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PANEL_ACTIVE) { - BLI_assert(panel->runtime.block != NULL); - panel_remove_invisible_layouts_recursive(panel, NULL); + BLI_assert(panel->runtime.block != nullptr); + panel_remove_invisible_layouts_recursive(panel, nullptr); } } } @@ -1054,7 +1055,7 @@ static void panel_draw_highlight_border(const Panel *panel, const rcti *rect, const rcti *header_rect) { - const bool is_subpanel = panel->type->parent != NULL; + const bool is_subpanel = panel->type->parent != nullptr; if (is_subpanel) { return; } @@ -1064,18 +1065,15 @@ static void panel_draw_highlight_border(const Panel *panel, const float radius = (btheme->tui.panel_roundness * U.widget_unit * 0.5f) / aspect; UI_draw_roundbox_corner_set(UI_CNR_ALL); + rctf box_rect; + box_rect.xmin = rect->xmin; + box_rect.xmax = rect->xmax; + box_rect.ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin; + box_rect.ymax = header_rect->ymax; + float color[4]; UI_GetThemeColor4fv(TH_SELECT_ACTIVE, color); - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = rect->xmin, - .xmax = rect->xmax, - .ymin = UI_panel_is_closed(panel) ? header_rect->ymin : rect->ymin, - .ymax = header_rect->ymax, - }, - false, - radius, - color); + UI_draw_roundbox_4fv(&box_rect, false, radius, color); } static void panel_draw_aligned_widgets(const uiStyle *style, @@ -1086,19 +1084,18 @@ static void panel_draw_aligned_widgets(const uiStyle *style, const bool show_background, const bool region_search_filter_active) { - const bool is_subpanel = panel->type->parent != NULL; + const bool is_subpanel = panel->type->parent != nullptr; const uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle; const int header_height = BLI_rcti_size_y(header_rect); const int scaled_unit = round_fl_to_int(UI_UNIT_X / aspect); - /* Offset triangle and text to the right for subpanels. */ - const rcti widget_rect = { - .xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0), - .xmax = header_rect->xmax, - .ymin = header_rect->ymin, - .ymax = header_rect->ymax, - }; + /* Offset triangle and text to the right for sub-panels. */ + rcti widget_rect; + widget_rect.xmin = header_rect->xmin + (is_subpanel ? scaled_unit * 0.7f : 0); + widget_rect.xmax = header_rect->xmax; + widget_rect.ymin = header_rect->ymin; + widget_rect.ymax = header_rect->ymax; uchar title_color[4]; panel_title_color_get(panel, show_background, region_search_filter_active, title_color); @@ -1121,20 +1118,16 @@ static void panel_draw_aligned_widgets(const uiStyle *style, /* Draw text label. */ if (panel->drawname[0] != '\0') { - const rcti title_rect = { - .xmin = widget_rect.xmin + (panel->labelofs / aspect) + scaled_unit * 1.1f, - .xmax = widget_rect.xmax, - .ymin = widget_rect.ymin - 2.0f / aspect, - .ymax = widget_rect.ymax, - }; - UI_fontstyle_draw(fontstyle, - &title_rect, - panel->drawname, - sizeof(panel->drawname), - title_color, - &(struct uiFontStyleDraw_Params){ - .align = UI_STYLE_TEXT_LEFT, - }); + rcti title_rect; + title_rect.xmin = widget_rect.xmin + (panel->labelofs / aspect) + scaled_unit * 1.1f; + title_rect.xmax = widget_rect.xmax; + title_rect.ymin = widget_rect.ymin - 2.0f / aspect; + title_rect.ymax = widget_rect.ymax; + + uiFontStyleDraw_Params params{}; + params.align = UI_STYLE_TEXT_LEFT; + UI_fontstyle_draw( + fontstyle, &title_rect, panel->drawname, sizeof(panel->drawname), title_color, ¶ms); } /* Draw the pin icon. */ @@ -1177,7 +1170,7 @@ static void panel_draw_aligned_backdrop(const Panel *panel, const rcti *rect, const rcti *header_rect) { - const bool is_subpanel = panel->type->parent != NULL; + const bool is_subpanel = panel->type->parent != nullptr; const bool is_open = !UI_panel_is_closed(panel); if (is_subpanel && !is_open) { @@ -1197,16 +1190,12 @@ static void panel_draw_aligned_backdrop(const Panel *panel, UI_draw_roundbox_corner_set(is_open ? UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT : UI_CNR_ALL); UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor); - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = rect->xmin, - .xmax = rect->xmax, - .ymin = rect->ymin, - .ymax = rect->ymax, - }, - true, - radius, - panel_backcolor); + rctf box_rect; + box_rect.xmin = rect->xmin; + box_rect.xmax = rect->xmax; + box_rect.ymin = rect->ymin; + box_rect.ymax = rect->ymax; + UI_draw_roundbox_4fv(&box_rect, true, radius, panel_backcolor); } /* Panel header backdrops for non sub-panels. */ @@ -1217,16 +1206,12 @@ static void panel_draw_aligned_backdrop(const Panel *panel, UI_draw_roundbox_corner_set(is_open ? UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT : UI_CNR_ALL); /* Change the width a little bit to line up with the sides. */ - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = rect->xmin, - .xmax = rect->xmax, - .ymin = header_rect->ymin, - .ymax = header_rect->ymax, - }, - true, - radius, - panel_headercolor); + rctf box_rect; + box_rect.xmin = rect->xmin; + box_rect.xmax = rect->xmax; + box_rect.ymin = header_rect->ymin; + box_rect.ymax = header_rect->ymax; + UI_draw_roundbox_4fv(&box_rect, true, radius, panel_headercolor); } GPU_blend(GPU_BLEND_NONE); @@ -1247,7 +1232,7 @@ void ui_draw_aligned_panel(const uiStyle *style, rect->xmin, rect->xmax, rect->ymax, - rect->ymax + floor(PNL_HEADER / block->aspect + 0.001f), + rect->ymax + (int)floor(PNL_HEADER / block->aspect + 0.001f), }; if (show_background) { @@ -1300,7 +1285,7 @@ bool UI_panel_should_show_background(const ARegion *region, const PanelType *pan void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) { // #define USE_FLAT_INACTIVE - const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment != RGN_ALIGN_RIGHT); + const bool is_left = RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT; View2D *v2d = ®ion->v2d; const uiStyle *style = UI_style_get(); const uiFontStyle *fstyle = &style->widget; @@ -1463,26 +1448,17 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) { /* Draw filled rectangle and outline for tab. */ UI_draw_roundbox_corner_set(roundboxtype); - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = rct->xmin, - .xmax = rct->xmax, - .ymin = rct->ymin, - .ymax = rct->ymax, - }, - true, - tab_curve_radius, - is_active ? theme_col_tab_active : theme_col_tab_inactive); - UI_draw_roundbox_4fv( - &(const rctf){ - .xmin = rct->xmin, - .xmax = rct->xmax, - .ymin = rct->ymin, - .ymax = rct->ymax, - }, - false, - tab_curve_radius, - theme_col_tab_outline); + rctf box_rect; + box_rect.xmin = rct->xmin; + box_rect.xmax = rct->xmax; + box_rect.ymin = rct->ymin; + box_rect.ymax = rct->ymax; + + UI_draw_roundbox_4fv(&box_rect, + true, + tab_curve_radius, + is_active ? theme_col_tab_active : theme_col_tab_inactive); + UI_draw_roundbox_4fv(&box_rect, false, tab_curve_radius, theme_col_tab_outline); /* Disguise the outline on one side to join the tab to the panel. */ pos = GPU_vertformat_attr_add( @@ -1502,7 +1478,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) if (do_scaletabs) { category_draw_len = BLF_width_to_strlen( - fontid, category_id_draw, category_draw_len, category_width, NULL); + fontid, category_id_draw, category_draw_len, category_width, nullptr); } BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); @@ -1660,7 +1636,7 @@ static bool uiAlignPanelStep(ARegion *region, const float factor, const bool dra LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PANEL_ACTIVE) { /* These panels should have types since they are currently displayed to the user. */ - BLI_assert(panel->type != NULL); + BLI_assert(panel->type != nullptr); active_panels_len++; } } @@ -1669,7 +1645,8 @@ static bool uiAlignPanelStep(ARegion *region, const float factor, const bool dra } /* Sort panels. */ - PanelSort *panel_sort = MEM_mallocN(sizeof(PanelSort) * active_panels_len, __func__); + PanelSort *panel_sort = static_cast<PanelSort *>( + MEM_mallocN(sizeof(PanelSort) * active_panels_len, __func__)); { PanelSort *ps = panel_sort; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { @@ -1791,7 +1768,7 @@ static void ui_panels_size(ARegion *region, int *r_x, int *r_y) static void ui_do_animate(bContext *C, Panel *panel) { - uiHandlePanelData *data = panel->activedata; + uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata); ARegion *region = CTX_wm_region(C); float fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME; @@ -1856,7 +1833,7 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PANEL_ACTIVE) { - BLI_assert(panel->runtime.block != NULL); + BLI_assert(panel->runtime.block != nullptr); panel_calculate_size_recursive(region, panel); } } @@ -1892,7 +1869,7 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) #define DRAG_REGION_PAD (PNL_HEADER * 0.5) static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel) { - uiHandlePanelData *data = panel->activedata; + uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata); ARegion *region = CTX_wm_region(C); /* Keep the drag position in the region with a small pad to keep the panel visible. */ @@ -1942,14 +1919,14 @@ static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block, return PANEL_MOUSE_OUTSIDE; } -typedef struct uiPanelDragCollapseHandle { +struct uiPanelDragCollapseHandle { bool was_first_open; int xy_init[2]; -} uiPanelDragCollapseHandle; +}; static void ui_panel_drag_collapse_handler_remove(bContext *UNUSED(C), void *userdata) { - uiPanelDragCollapseHandle *dragcol_data = userdata; + uiPanelDragCollapseHandle *dragcol_data = static_cast<uiPanelDragCollapseHandle *>(userdata); MEM_freeN(dragcol_data); } @@ -1960,11 +1937,11 @@ static void ui_panel_drag_collapse(const bContext *C, ARegion *region = CTX_wm_region(C); LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)}; - float xy_b_block[2] = {UNPACK2(xy_dst)}; + float xy_a_block[2] = {(float)dragcol_data->xy_init[0], (float)dragcol_data->xy_init[1]}; + float xy_b_block[2] = {(float)xy_dst[0], (float)xy_dst[1]}; Panel *panel = block->panel; - if (panel == NULL || (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER))) { + if (panel == nullptr || (panel->type && (panel->type->flag & PANEL_TYPE_NO_HEADER))) { continue; } const int oldflag = panel->flag; @@ -2006,7 +1983,7 @@ static void ui_panel_drag_collapse(const bContext *C, static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, void *userdata) { wmWindow *win = CTX_wm_window(C); - uiPanelDragCollapseHandle *dragcol_data = userdata; + uiPanelDragCollapseHandle *dragcol_data = static_cast<uiPanelDragCollapseHandle *>(userdata); short retval = WM_UI_HANDLER_CONTINUE; switch (event->type) { @@ -2037,7 +2014,7 @@ static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was { wmWindow *win = CTX_wm_window(C); const wmEvent *event = win->eventstate; - uiPanelDragCollapseHandle *dragcol_data = MEM_mallocN(sizeof(*dragcol_data), __func__); + uiPanelDragCollapseHandle *dragcol_data = MEM_new<uiPanelDragCollapseHandle>(__func__); dragcol_data->was_first_open = was_open; copy_v2_v2_int(dragcol_data->xy_init, event->xy); @@ -2066,10 +2043,10 @@ static void ui_handle_panel_header(const bContext *C, Panel *panel = block->panel; ARegion *region = CTX_wm_region(C); - BLI_assert(panel->type != NULL); + BLI_assert(panel->type != nullptr); BLI_assert(!(panel->type->flag & PANEL_TYPE_NO_HEADER)); - const bool is_subpanel = (panel->type->parent != NULL); + const bool is_subpanel = (panel->type->parent != nullptr); const bool use_pin = UI_panel_category_is_visible(region) && UI_panel_can_be_pinned(panel); const bool show_pin = use_pin && (panel->flag & PNL_PIN); const bool show_drag = !is_subpanel; @@ -2102,8 +2079,8 @@ static void ui_handle_panel_header(const bContext *C, else { /* If a panel has sub-panels and it's open, toggle the expansion * of the sub-panels (based on the expansion of the first sub-panel). */ - Panel *first_child = panel->children.first; - BLI_assert(first_child != NULL); + Panel *first_child = static_cast<Panel *>(panel->children.first); + BLI_assert(first_child != nullptr); panel_set_flag_recursive(panel, PNL_CLOSED, !UI_panel_is_closed(first_child)); panel->flag |= PNL_CLOSED; } @@ -2115,7 +2092,7 @@ static void ui_handle_panel_header(const bContext *C, ui_panel_drag_collapse_handler_add(C, UI_panel_is_closed(panel)); } - /* Set panel custom data (modifier) active when expanding subpanels, but not top-level + /* Set panel custom data (modifier) active when expanding sub-panels, but not top-level * panels to allow collapsing and expanding without setting the active element. */ if (is_subpanel) { panel_custom_data_active_set(panel); @@ -2157,13 +2134,14 @@ bool UI_panel_category_is_visible(const ARegion *region) PanelCategoryDyn *UI_panel_category_find(const ARegion *region, const char *idname) { - return BLI_findstring(®ion->panels_category, idname, offsetof(PanelCategoryDyn, idname)); + return static_cast<PanelCategoryDyn *>( + BLI_findstring(®ion->panels_category, idname, offsetof(PanelCategoryDyn, idname))); } PanelCategoryStack *UI_panel_category_active_find(ARegion *region, const char *idname) { - return BLI_findstring( - ®ion->panels_category_active, idname, offsetof(PanelCategoryStack, idname)); + return static_cast<PanelCategoryStack *>(BLI_findstring( + ®ion->panels_category_active, idname, offsetof(PanelCategoryStack, idname))); } static void ui_panel_category_active_set(ARegion *region, const char *idname, bool fallback) @@ -2175,7 +2153,7 @@ static void ui_panel_category_active_set(ARegion *region, const char *idname, bo BLI_remlink(lb, pc_act); } else { - pc_act = MEM_callocN(sizeof(PanelCategoryStack), __func__); + pc_act = MEM_cnew<PanelCategoryStack>(__func__); BLI_strncpy(pc_act->idname, idname, sizeof(pc_act->idname)); } @@ -2226,14 +2204,14 @@ const char *UI_panel_category_active_get(ARegion *region, bool set_fallback) } if (set_fallback) { - PanelCategoryDyn *pc_dyn = region->panels_category.first; + PanelCategoryDyn *pc_dyn = static_cast<PanelCategoryDyn *>(region->panels_category.first); if (pc_dyn) { ui_panel_category_active_set(region, pc_dyn->idname, true); return pc_dyn->idname; } } - return NULL; + return nullptr; } static PanelCategoryDyn *panel_categories_find_mouse_over(ARegion *region, const wmEvent *event) @@ -2244,12 +2222,12 @@ static PanelCategoryDyn *panel_categories_find_mouse_over(ARegion *region, const } } - return NULL; + return nullptr; } void UI_panel_category_add(ARegion *region, const char *name) { - PanelCategoryDyn *pc_dyn = MEM_callocN(sizeof(*pc_dyn), __func__); + PanelCategoryDyn *pc_dyn = MEM_cnew<PanelCategoryDyn>(__func__); BLI_addtail(®ion->panels_category, pc_dyn); BLI_strncpy(pc_dyn->idname, name, sizeof(pc_dyn->idname)); @@ -2294,7 +2272,8 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, pc_dyn = backwards ? pc_dyn->prev : pc_dyn->next; if (!pc_dyn) { /* Proper cyclic behavior, back to first/last category (only used for ctrl+tab). */ - pc_dyn = backwards ? region->panels_category.last : region->panels_category.first; + pc_dyn = backwards ? static_cast<PanelCategoryDyn *>(region->panels_category.last) : + static_cast<PanelCategoryDyn *>(region->panels_category.first); } } @@ -2359,11 +2338,11 @@ int ui_handler_panel_region(bContext *C, return retval; } - const bool region_has_active_button = (ui_region_find_active_but(region) != NULL); + const bool region_has_active_button = (ui_region_find_active_but(region) != nullptr); LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { Panel *panel = block->panel; - if (panel == NULL || panel->type == NULL) { + if (panel == nullptr || panel->type == nullptr) { continue; } /* We can't expand or collapse panels without headers, they would disappear. */ @@ -2434,10 +2413,10 @@ void UI_panel_context_pointer_set(Panel *panel, const char *name, PointerRNA *pt void UI_panel_custom_data_set(Panel *panel, PointerRNA *custom_data) { - BLI_assert(panel->type != NULL); + BLI_assert(panel->type != nullptr); /* Free the old custom data, which should be shared among all of the panel's sub-panels. */ - if (panel->runtime.custom_data_ptr != NULL) { + if (panel->runtime.custom_data_ptr != nullptr) { MEM_freeN(panel->runtime.custom_data_ptr); } @@ -2455,7 +2434,7 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { Panel *panel = block->panel; - if (panel == NULL) { + if (panel == nullptr) { continue; } @@ -2468,12 +2447,12 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm } } - return NULL; + return nullptr; } bool UI_panel_can_be_pinned(const Panel *panel) { - return (panel->type->parent == NULL) && !(panel->type->flag & PANEL_TYPE_INSTANCED); + return (panel->type->parent == nullptr) && !(panel->type->flag & PANEL_TYPE_INSTANCED); } /** \} */ @@ -2485,8 +2464,8 @@ bool UI_panel_can_be_pinned(const Panel *panel) /* NOTE: this is modal handler and should not swallow events for animation. */ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) { - Panel *panel = userdata; - uiHandlePanelData *data = panel->activedata; + Panel *panel = static_cast<Panel *>(userdata); + uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata); /* Verify if we can stop. */ if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { @@ -2506,7 +2485,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) } } - data = panel->activedata; + data = static_cast<uiHandlePanelData *>(panel->activedata); if (data && data->state == PANEL_STATE_ANIMATION) { return WM_UI_HANDLER_CONTINUE; @@ -2516,7 +2495,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) static void ui_handler_remove_panel(bContext *C, void *userdata) { - Panel *panel = userdata; + Panel *panel = static_cast<Panel *>(userdata); panel_activate_state(C, panel, PANEL_STATE_EXIT); } @@ -2527,13 +2506,13 @@ static void panel_handle_data_ensure(const bContext *C, Panel *panel, const uiHandlePanelState state) { - if (panel->activedata == NULL) { + if (panel->activedata == nullptr) { panel->activedata = MEM_callocN(sizeof(uiHandlePanelData), __func__); WM_event_add_ui_handler( C, &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, panel, 0); } - uiHandlePanelData *data = panel->activedata; + uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata); data->animtimer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, ANIMATION_INTERVAL); @@ -2554,11 +2533,11 @@ static void panel_handle_data_ensure(const bContext *C, */ static void panel_activate_state(const bContext *C, Panel *panel, const uiHandlePanelState state) { - uiHandlePanelData *data = panel->activedata; + uiHandlePanelData *data = static_cast<uiHandlePanelData *>(panel->activedata); wmWindow *win = CTX_wm_window(C); ARegion *region = CTX_wm_region(C); - if (data != NULL && data->state == state) { + if (data != nullptr && data->state == state) { return; } @@ -2582,15 +2561,15 @@ static void panel_activate_state(const bContext *C, Panel *panel, const uiHandle else if (state == PANEL_STATE_EXIT) { panel_set_runtime_flag_recursive(panel, PANEL_IS_DRAG_DROP, false); - BLI_assert(data != NULL); + BLI_assert(data != nullptr); if (data->animtimer) { WM_event_remove_timer(CTX_wm_manager(C), win, data->animtimer); - data->animtimer = NULL; + data->animtimer = nullptr; } MEM_freeN(data); - panel->activedata = NULL; + panel->activedata = nullptr; WM_event_remove_ui_handler( &win->modalhandlers, ui_handler_panel, ui_handler_remove_panel, panel, false); diff --git a/source/blender/editors/interface/interface_region_menu_pie.cc b/source/blender/editors/interface/interface_region_menu_pie.cc index 8572e938b30..becdfaf4e25 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.cc +++ b/source/blender/editors/interface/interface_region_menu_pie.cc @@ -37,7 +37,7 @@ #include "ED_screen.h" #include "interface_intern.h" -#include "interface_regions_intern.h" +#include "interface_regions_intern.hh" /* -------------------------------------------------------------------- */ /** \name Pie Menu diff --git a/source/blender/editors/interface/interface_region_menu_popup.cc b/source/blender/editors/interface/interface_region_menu_popup.cc index a22f7218203..0647e1a4a70 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.cc +++ b/source/blender/editors/interface/interface_region_menu_popup.cc @@ -39,7 +39,7 @@ #include "ED_screen.h" #include "interface_intern.h" -#include "interface_regions_intern.h" +#include "interface_regions_intern.hh" /* -------------------------------------------------------------------- */ /** \name Utility Functions diff --git a/source/blender/editors/interface/interface_region_popover.cc b/source/blender/editors/interface/interface_region_popover.cc index 2e10261a4f7..17c8d890755 100644 --- a/source/blender/editors/interface/interface_region_popover.cc +++ b/source/blender/editors/interface/interface_region_popover.cc @@ -46,7 +46,7 @@ #include "UI_interface.h" #include "interface_intern.h" -#include "interface_regions_intern.h" +#include "interface_regions_intern.hh" /* -------------------------------------------------------------------- */ /** \name Popup Menu with Callback or String @@ -397,7 +397,7 @@ void UI_popover_end(bContext *C, uiPopover *pup, wmKeyMap *keymap) pup->window = window; - /* TODO(campbell): we may want to make this configurable. + /* TODO(@campbellbarton): we may want to make this configurable. * The begin/end stype of calling popups doesn't allow 'can_refresh' to be set. * For now close this style of popovers when accessed. */ UI_block_flag_disable(pup->block, UI_BLOCK_KEEP_OPEN); diff --git a/source/blender/editors/interface/interface_region_popup.cc b/source/blender/editors/interface/interface_region_popup.cc index 74c228e3338..f6b078c033e 100644 --- a/source/blender/editors/interface/interface_region_popup.cc +++ b/source/blender/editors/interface/interface_region_popup.cc @@ -31,7 +31,7 @@ #include "ED_screen.h" #include "interface_intern.h" -#include "interface_regions_intern.h" +#include "interface_regions_intern.hh" /* -------------------------------------------------------------------- */ /** \name Utility Functions diff --git a/source/blender/editors/interface/interface_region_search.cc b/source/blender/editors/interface/interface_region_search.cc index 81c0c29d09a..6bb47666afd 100644 --- a/source/blender/editors/interface/interface_region_search.cc +++ b/source/blender/editors/interface/interface_region_search.cc @@ -41,7 +41,7 @@ #include "GPU_state.h" #include "interface_intern.h" -#include "interface_regions_intern.h" +#include "interface_regions_intern.hh" #define MENU_BORDER (int)(0.3f * U.widget_unit) @@ -710,18 +710,18 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, type.regionid = RGN_TYPE_TEMPORARY; region->type = &type; - /* create searchbox data */ + /* Create search-box data. */ uiSearchboxData *data = MEM_cnew<uiSearchboxData>(__func__); - /* set font, get bb */ + /* Set font, get the bounding-box. */ data->fstyle = style->widget; /* copy struct */ ui_fontscale(&data->fstyle.points, aspect); UI_fontstyle_set(&data->fstyle); region->regiondata = data; - /* special case, hardcoded feature, not draw backdrop when called from menus, - * assume for design that popup already added it */ + /* Special case, hard-coded feature, not draw backdrop when called from menus, + * assume for design that popup already added it. */ if (but->block->flag & UI_BLOCK_SEARCH_MENU) { data->noback = true; } diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.cc index f460a159a5f..8d88261c328 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.cc @@ -7,7 +7,7 @@ * ToolTip Region and Construction */ -/* TODO(campbell): +/* TODO(@campbellbarton): * We may want to have a higher level API that initializes a timer, * checks for mouse motion and clears the tool-tip afterwards. * We never want multiple tool-tips at once @@ -16,9 +16,9 @@ * For now it's not a priority, so leave as-is. */ -#include <stdarg.h> -#include <stdlib.h> -#include <string.h> +#include <cstdarg> +#include <cstdlib> +#include <cstring> #include "MEM_guardedalloc.h" @@ -53,7 +53,7 @@ #include "ED_screen.h" #include "interface_intern.h" -#include "interface_regions_intern.h" +#include "interface_regions_intern.hh" #define UI_TIP_PAD_FAC 1.3f #define UI_TIP_PADDING (int)(UI_TIP_PAD_FAC * UI_UNIT_Y) @@ -61,59 +61,82 @@ #define UI_TIP_STR_MAX 1024 -typedef struct uiTooltipFormat { - enum { - UI_TIP_STYLE_NORMAL = 0, - UI_TIP_STYLE_HEADER, - UI_TIP_STYLE_MONO, - } style : 3; - enum { - UI_TIP_LC_MAIN = 0, /* primary text */ - UI_TIP_LC_VALUE, /* the value of buttons (also shortcuts) */ - UI_TIP_LC_ACTIVE, /* titles of active enum values */ - UI_TIP_LC_NORMAL, /* regular text */ - UI_TIP_LC_PYTHON, /* Python snippet */ - UI_TIP_LC_ALERT, /* description of why operator can't run */ - } color_id : 4; - int is_pad : 1; -} uiTooltipFormat; - -typedef struct uiTooltipField { +struct uiTooltipFormat { + enum class Style : int8_t { + Normal, + Header, + Mono, + }; + enum class ColorID : int8_t { + /** Primary Text. */ + Main = 0, + /** The value of buttons (also shortcuts). */ + Value = 1, + /** Titles of active enum values. */ + Active = 2, + /** Regular text. */ + Normal = 3, + /** Python snippet. */ + Python = 4, + /** Description of why an operator can't run. */ + Alert = 5, + }; + Style style; + ColorID color_id; + bool is_pad; +}; + +struct uiTooltipField { char *text; char *text_suffix; struct { - uint x_pos; /* x cursor position at the end of the last line */ - uint lines; /* number of lines, 1 or more with word-wrap */ + /** X cursor position at the end of the last line. */ + uint x_pos; + /** Number of lines, 1 or more with word-wrap. */ + uint lines; } geom; uiTooltipFormat format; +}; -} uiTooltipField; - -typedef struct uiTooltipData { +struct uiTooltipData { rcti bbox; uiTooltipField *fields; uint fields_len; uiFontStyle fstyle; int wrap_width; int toth, lineh; -} uiTooltipData; +}; #define UI_TIP_LC_MAX 6 -BLI_STATIC_ASSERT(UI_TIP_LC_MAX == UI_TIP_LC_ALERT + 1, "invalid lc-max"); +BLI_STATIC_ASSERT(UI_TIP_LC_MAX == static_cast<int>(uiTooltipFormat::ColorID::Alert) + 1, + "invalid lc-max"); BLI_STATIC_ASSERT(sizeof(uiTooltipFormat) <= sizeof(int), "oversize"); static uiTooltipField *text_field_add_only(uiTooltipData *data) { data->fields_len += 1; - data->fields = MEM_recallocN(data->fields, sizeof(*data->fields) * data->fields_len); + data->fields = static_cast<uiTooltipField *>( + MEM_recallocN(data->fields, sizeof(*data->fields) * data->fields_len)); return &data->fields[data->fields_len - 1]; } -static uiTooltipField *text_field_add(uiTooltipData *data, const uiTooltipFormat *format) +// static uiTooltipField *text_field_add(uiTooltipData *data, const uiTooltipFormat *format) +// { +// uiTooltipField *field = text_field_add_only(data); +// field->format = *format; +// return field; +// } + +static uiTooltipField *text_field_add(uiTooltipData *data, + const uiTooltipFormat::Style style, + const uiTooltipFormat::ColorID color, + const bool is_pad = false) { uiTooltipField *field = text_field_add_only(data); - field->format = *format; + field->format = {}; + field->format.style = style; + field->format.color_id = color, field->format.is_pad = is_pad; return field; } @@ -138,30 +161,31 @@ static void rgb_tint(float col[3], float h, float h_strength, float v, float v_s static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region) { const float pad_px = UI_TIP_PADDING; - uiTooltipData *data = region->regiondata; + uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata); const uiWidgetColors *theme = ui_tooltip_get_theme(); rcti bbox = data->bbox; float tip_colors[UI_TIP_LC_MAX][3]; uchar drawcol[4] = {0, 0, 0, 255}; /* to store color in while drawing (alpha is always 255) */ - float *main_color = tip_colors[UI_TIP_LC_MAIN]; /* the color from the theme */ - float *value_color = tip_colors[UI_TIP_LC_VALUE]; - float *active_color = tip_colors[UI_TIP_LC_ACTIVE]; - float *normal_color = tip_colors[UI_TIP_LC_NORMAL]; - float *python_color = tip_colors[UI_TIP_LC_PYTHON]; - float *alert_color = tip_colors[UI_TIP_LC_ALERT]; + /* The color from the theme. */ + float *main_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Main)]; + float *value_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Value)]; + float *active_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Active)]; + float *normal_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Normal)]; + float *python_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Python)]; + float *alert_color = tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Alert)]; float background_color[3]; wmOrtho2_region_pixelspace(region); - /* draw background */ - ui_draw_tooltip_background(UI_style_get(), NULL, &bbox); + /* Draw background. */ + ui_draw_tooltip_background(UI_style_get(), nullptr, &bbox); /* set background_color */ rgb_uchar_to_float(background_color, theme->inner); - /* calculate normal_color */ + /* Calculate `normal_color`. */ rgb_uchar_to_float(main_color, theme->text); copy_v3_v3(active_color, main_color); copy_v3_v3(normal_color, main_color); @@ -169,19 +193,19 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region copy_v3_v3(alert_color, main_color); copy_v3_v3(value_color, main_color); - /* find the brightness difference between background and text colors */ + /* Find the brightness difference between background and text colors. */ const float tone_bg = rgb_to_grayscale(background_color); - /* tone_fg = rgb_to_grayscale(main_color); */ + // tone_fg = rgb_to_grayscale(main_color); - /* mix the colors */ + /* Mix the colors. */ rgb_tint(value_color, 0.0f, 0.0f, tone_bg, 0.2f); /* Light gray. */ rgb_tint(active_color, 0.6f, 0.2f, tone_bg, 0.2f); /* Light blue. */ rgb_tint(normal_color, 0.0f, 0.0f, tone_bg, 0.4f); /* Gray. */ rgb_tint(python_color, 0.0f, 0.0f, tone_bg, 0.5f); /* Dark gray. */ rgb_tint(alert_color, 0.0f, 0.8f, tone_bg, 0.1f); /* Red. */ - /* draw text */ + /* Draw text. */ BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width); BLF_wordwrap(blf_mono_font, data->wrap_width); @@ -190,58 +214,58 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region for (int i = 0; i < data->fields_len; i++) { const uiTooltipField *field = &data->fields[i]; - const uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : NULL; + const uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : + nullptr; bbox.ymin = bbox.ymax - (data->lineh * field->geom.lines); - if (field->format.style == UI_TIP_STYLE_HEADER) { - const struct uiFontStyleDraw_Params fs_params = { - .align = UI_STYLE_TEXT_LEFT, - .word_wrap = true, - }; - /* draw header and active data (is done here to be able to change color) */ - rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_MAIN]); + if (field->format.style == uiTooltipFormat::Style::Header) { + uiFontStyleDraw_Params fs_params{}; + fs_params.align = UI_STYLE_TEXT_LEFT; + fs_params.word_wrap = true; + + /* Draw header and active data (is done here to be able to change color). */ + rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Main)]); UI_fontstyle_set(&data->fstyle); UI_fontstyle_draw(&data->fstyle, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params); - /* offset to the end of the last line */ + /* Offset to the end of the last line. */ if (field->text_suffix) { const float xofs = field->geom.x_pos; const float yofs = data->lineh * (field->geom.lines - 1); bbox.xmin += xofs; bbox.ymax -= yofs; - rgb_float_to_uchar(drawcol, tip_colors[UI_TIP_LC_ACTIVE]); + rgb_float_to_uchar(drawcol, + tip_colors[static_cast<int>(uiTooltipFormat::ColorID::Active)]); UI_fontstyle_draw( &data->fstyle, &bbox, field->text_suffix, UI_TIP_STR_MAX, drawcol, &fs_params); - /* undo offset */ + /* Undo offset. */ bbox.xmin -= xofs; bbox.ymax += yofs; } } - else if (field->format.style == UI_TIP_STYLE_MONO) { - const struct uiFontStyleDraw_Params fs_params = { - .align = UI_STYLE_TEXT_LEFT, - .word_wrap = true, - }; + else if (field->format.style == uiTooltipFormat::Style::Mono) { + uiFontStyleDraw_Params fs_params{}; + fs_params.align = UI_STYLE_TEXT_LEFT; + fs_params.word_wrap = true; uiFontStyle fstyle_mono = data->fstyle; fstyle_mono.uifont_id = blf_mono_font; UI_fontstyle_set(&fstyle_mono); - /* XXX, needed because we don't have mono in 'U.uifonts' */ + /* XXX: needed because we don't have mono in 'U.uifonts'. */ BLF_size(fstyle_mono.uifont_id, fstyle_mono.points * U.pixelsize, U.dpi); - rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]); + rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(field->format.color_id)]); UI_fontstyle_draw(&fstyle_mono, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params); } else { - BLI_assert(field->format.style == UI_TIP_STYLE_NORMAL); - const struct uiFontStyleDraw_Params fs_params = { - .align = UI_STYLE_TEXT_LEFT, - .word_wrap = true, - }; - - /* draw remaining data */ - rgb_float_to_uchar(drawcol, tip_colors[field->format.color_id]); + BLI_assert(field->format.style == uiTooltipFormat::Style::Normal); + uiFontStyleDraw_Params fs_params{}; + fs_params.align = UI_STYLE_TEXT_LEFT; + fs_params.word_wrap = true; + + /* Draw remaining data. */ + rgb_float_to_uchar(drawcol, tip_colors[static_cast<int>(field->format.color_id)]); UI_fontstyle_set(&data->fstyle); UI_fontstyle_draw(&data->fstyle, &bbox, field->text, UI_TIP_STR_MAX, drawcol, &fs_params); } @@ -259,7 +283,7 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region static void ui_tooltip_region_free_cb(ARegion *region) { - uiTooltipData *data = region->regiondata; + uiTooltipData *data = static_cast<uiTooltipData *>(region->regiondata); for (int i = 0; i < data->fields_len; i++) { const uiTooltipField *field = &data->fields[i]; @@ -270,7 +294,7 @@ static void ui_tooltip_region_free_cb(ARegion *region) } MEM_freeN(data->fields); MEM_freeN(data); - region->regiondata = NULL; + region->regiondata = nullptr; } /** \} */ @@ -281,7 +305,7 @@ static void ui_tooltip_region_free_cb(ARegion *region) static char *ui_tooltip_text_python_from_op(bContext *C, wmOperatorType *ot, PointerRNA *opptr) { - char *str = WM_operator_pystring_ex(C, NULL, false, false, ot, opptr); + char *str = WM_operator_pystring_ex(C, nullptr, false, false, ot, opptr); /* Avoid overly verbose tips (eg, arrays of 20 layers), exact limit is arbitrary. */ WM_operator_pystring_abbreviate(str, 32); @@ -304,24 +328,17 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data, LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { wmOperatorType *ot = WM_operatortype_find(kmi->idname, true); - if (ot != NULL) { - /* Tip */ + if (ot != nullptr) { + /* Tip. */ { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_MAIN, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Main, true); field->text = BLI_strdup(ot->description ? ot->description : ot->name); } - /* Shortcut */ + /* Shortcut. */ { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_NORMAL, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal); bool found = false; if (WM_keymap_item_to_string(kmi, false, buf, sizeof(buf))) { found = true; @@ -329,13 +346,10 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data, field->text = BLI_sprintfN(TIP_("Shortcut: %s"), found ? buf : "None"); } - /* Python */ + /* Python. */ if (U.flag & USER_TOOLTIPS_PYTHON) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_PYTHON, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Python); char *str = ui_tooltip_text_python_from_op(C, ot, kmi->ptr); field->text = BLI_sprintfN(TIP_("Python: %s"), str); MEM_freeN(str); @@ -353,17 +367,17 @@ static bool ui_tooltip_data_append_from_keymap(bContext *C, uiTooltipData *data, */ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is_label) { - if (but->optype == NULL) { - return NULL; + if (but->optype == nullptr) { + return nullptr; } if (!STREQ(but->optype->idname, "WM_OT_tool_set_by_id")) { - return NULL; + return nullptr; } /* Needed to get the space-data's type (below). */ - if (CTX_wm_space_data(C) == NULL) { - return NULL; + if (CTX_wm_space_data(C) == nullptr) { + return nullptr; } char tool_id[MAX_NAME]; @@ -378,7 +392,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is const char *has_valid_context_error = IFACE_("Unsupported context"); { ScrArea *area = CTX_wm_area(C); - if (area == NULL) { + if (area == nullptr) { has_valid_context = false; } else { @@ -393,16 +407,15 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is } /* We have a tool, now extract the info. */ - uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); + uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__); #ifdef WITH_PYTHON - /* it turns out to be most simple to do this via Python since C - * doesn't have access to information about non-active tools. - */ + /* It turns out to be most simple to do this via Python since C + * doesn't have access to information about non-active tools. */ /* Title (when icon-only). */ if (but->drawstr[0] == '\0') { - const char *expr_imports[] = {"bpy", "bl_ui", NULL}; + const char *expr_imports[] = {"bpy", "bl_ui", nullptr}; char expr[256]; SNPRINTF(expr, "bl_ui.space_toolsystem_common.item_from_id(" @@ -410,16 +423,16 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is "bpy.context.space_data.type, " "'%s').label", tool_id); - char *expr_result = NULL; + char *expr_result = nullptr; bool is_error = false; if (has_valid_context == false) { expr_result = BLI_strdup(has_valid_context_error); } - else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) { + else if (BPY_run_string_as_string(C, expr_imports, expr, nullptr, &expr_result)) { if (STREQ(expr_result, "")) { MEM_freeN(expr_result); - expr_result = NULL; + expr_result = nullptr; } } else { @@ -429,7 +442,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is is_error = true; } - if (expr_result != NULL) { + if (expr_result != nullptr) { /* NOTE: This is a very weak hack to get a valid translation most of the time... * Proper way to do would be to get i18n context from the item, somehow. */ const char *label_str = CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, expr_result); @@ -442,23 +455,19 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is expr_result = BLI_strdup(label_str); } - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_MAIN, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Main, true); field->text = expr_result; if (UNLIKELY(is_error)) { - field->format.color_id = UI_TIP_LC_ALERT; + field->format.color_id = uiTooltipFormat::ColorID::Alert; } } } /* Tip. */ if (is_label == false) { - const char *expr_imports[] = {"bpy", "bl_ui", NULL}; + const char *expr_imports[] = {"bpy", "bl_ui", nullptr}; char expr[256]; SNPRINTF(expr, "bl_ui.space_toolsystem_common.description_from_id(" @@ -467,16 +476,16 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is "'%s') + '.'", tool_id); - char *expr_result = NULL; + char *expr_result = nullptr; bool is_error = false; if (has_valid_context == false) { expr_result = BLI_strdup(has_valid_context_error); } - else if (BPY_run_string_as_string(C, expr_imports, expr, NULL, &expr_result)) { + else if (BPY_run_string_as_string(C, expr_imports, expr, nullptr, &expr_result)) { if (STREQ(expr_result, ".")) { MEM_freeN(expr_result); - expr_result = NULL; + expr_result = nullptr; } } else { @@ -486,17 +495,13 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is is_error = true; } - if (expr_result != NULL) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_MAIN, - .is_pad = true, - }); + if (expr_result != nullptr) { + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Main, true); field->text = expr_result; if (UNLIKELY(is_error)) { - field->format.color_id = UI_TIP_LC_ALERT; + field->format.color_id = uiTooltipFormat::ColorID::Alert; } } } @@ -515,18 +520,18 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is * * Either way case it's useful to show the shortcut. */ - char *shortcut = NULL; + char *shortcut = nullptr; { - uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL}; - UI_but_string_info_get(C, but, &op_keymap, NULL); + uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, nullptr}; + UI_but_string_info_get(C, but, &op_keymap, nullptr); shortcut = op_keymap.strinfo; } - if (shortcut == NULL) { + if (shortcut == nullptr) { const ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode); - if (tool_attr != NULL) { + if (tool_attr != nullptr) { const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); const char *tool_id_lstrip = strrchr(tool_id, '.'); const int tool_id_offset = tool_id_lstrip ? ((tool_id_lstrip - tool_id) + 1) : 0; @@ -543,7 +548,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (WM_key_event_operator_string(C, ot->idname, WM_OP_INVOKE_REGION_WIN, - op_props.data, + static_cast<IDProperty *>(op_props.data), true, shortcut_brush, ARRAY_SIZE(shortcut_brush))) { @@ -554,20 +559,20 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is } } - if (shortcut == NULL) { + if (shortcut == nullptr) { /* Check for direct access to the tool. */ char shortcut_toolbar[128] = ""; if (WM_key_event_operator_string(C, "WM_OT_toolbar", WM_OP_INVOKE_REGION_WIN, - NULL, + nullptr, true, shortcut_toolbar, ARRAY_SIZE(shortcut_toolbar))) { /* Generate keymap in order to inspect it. * NOTE: we could make a utility to avoid the keymap generation part of this. */ const char *expr_imports[] = { - "bpy", "bl_keymap_utils", "bl_keymap_utils.keymap_from_toolbar", NULL}; + "bpy", "bl_keymap_utils", "bl_keymap_utils.keymap_from_toolbar", nullptr}; const char *expr = ("getattr(" "bl_keymap_utils.keymap_from_toolbar.generate(" @@ -580,7 +585,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { shortcut = BLI_strdup(has_valid_context_error); } - else if (BPY_run_string_as_intptr(C, expr_imports, expr, NULL, &expr_result)) { + else if (BPY_run_string_as_intptr(C, expr_imports, expr, nullptr, &expr_result)) { if (expr_result != 0) { wmKeyMap *keymap = (wmKeyMap *)expr_result; LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { @@ -603,13 +608,9 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is } } - if (shortcut != NULL) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + if (shortcut != nullptr) { + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_sprintfN(TIP_("Shortcut: %s"), shortcut); MEM_freeN(shortcut); } @@ -627,11 +628,11 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is * This is a little involved since the shortcut may be bound to another tool in this group, * instead of the current tool on display. */ - char *expr_result = NULL; + char *expr_result = nullptr; size_t expr_result_len; { - const char *expr_imports[] = {"bpy", "bl_ui", NULL}; + const char *expr_imports[] = {"bpy", "bl_ui", nullptr}; char expr[256]; SNPRINTF(expr, "'\\x00'.join(" @@ -645,12 +646,12 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* pass */ } else if (BPY_run_string_as_string_and_size( - C, expr_imports, expr, NULL, &expr_result, &expr_result_len)) { + C, expr_imports, expr, nullptr, &expr_result, &expr_result_len)) { /* pass. */ } } - if (expr_result != NULL) { + if (expr_result != nullptr) { PointerRNA op_props; WM_operator_properties_create_ptr(&op_props, but->optype); RNA_boolean_set(&op_props, "cycle", true); @@ -665,7 +666,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (WM_key_event_operator_string(C, but->optype->idname, WM_OP_INVOKE_REGION_WIN, - op_props.data, + static_cast<IDProperty *>(op_props.data), true, shortcut, ARRAY_SIZE(shortcut))) { @@ -678,12 +679,8 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is MEM_freeN(expr_result); if (shortcut[0] != '\0') { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_sprintfN(TIP_("Shortcut Cycle: %s"), shortcut); } } @@ -691,12 +688,8 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* Python */ if ((is_label == false) && (U.flag & USER_TOOLTIPS_PYTHON)) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_PYTHON, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Python, true); char *str = ui_tooltip_text_python_from_op(C, but->optype, but->opptr); field->text = BLI_sprintfN(TIP_("Python: %s"), str); MEM_freeN(str); @@ -706,7 +699,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is /* This is too handy not to expose somehow, let's be sneaky for now. */ if ((is_label == false) && CTX_wm_window(C)->eventstate->modifier & KM_SHIFT) { - const char *expr_imports[] = {"bpy", "bl_ui", NULL}; + const char *expr_imports[] = {"bpy", "bl_ui", nullptr}; char expr[256]; SNPRINTF(expr, "getattr(" @@ -722,15 +715,11 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (has_valid_context == false) { /* pass */ } - else if (BPY_run_string_as_intptr(C, expr_imports, expr, NULL, &expr_result)) { + else if (BPY_run_string_as_intptr(C, expr_imports, expr, nullptr, &expr_result)) { if (expr_result != 0) { { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_NORMAL, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal, true); field->text = BLI_strdup("Tool Keymap:"); } wmKeyMap *keymap = (wmKeyMap *)expr_result; @@ -747,7 +736,7 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is if (data->fields_len == 0) { MEM_freeN(data); - return NULL; + return nullptr; } return data; } @@ -756,26 +745,26 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, uiBut *but, uiButExtraOpIcon *extra_icon) { - uiStringInfo but_label = {BUT_GET_LABEL, NULL}; - uiStringInfo but_tip = {BUT_GET_TIP, NULL}; - uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; - uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; - uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL}; - uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, NULL}; - uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; - uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; + uiStringInfo but_label = {BUT_GET_LABEL, nullptr}; + uiStringInfo but_tip = {BUT_GET_TIP, nullptr}; + uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, nullptr}; + uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, nullptr}; + uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, nullptr}; + uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, nullptr}; + uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, nullptr}; + uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, nullptr}; char buf[512]; wmOperatorType *optype = extra_icon ? UI_but_extra_operator_icon_optype_get(extra_icon) : but->optype; - PropertyRNA *rnaprop = extra_icon ? NULL : but->rnaprop; + PropertyRNA *rnaprop = extra_icon ? nullptr : but->rnaprop; /* create tooltip data */ - uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); + uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__); if (extra_icon) { - UI_but_extra_icon_string_info_get(C, extra_icon, &but_label, &but_tip, &op_keymap, NULL); + UI_but_extra_icon_string_info_get(C, extra_icon, &but_label, &but_tip, &op_keymap, nullptr); } else { UI_but_string_info_get(C, @@ -788,30 +777,25 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, &prop_keymap, &rna_struct, &rna_prop, - NULL); + nullptr); } /* Tip Label (only for buttons not already showing the label). * Check prefix instead of comparing because the button may include the shortcut. - * Buttons with dynamic tooltips also don't get their default label here since they - * can already provide more accurate and specific tooltip content. */ + * Buttons with dynamic tool-tips also don't get their default label here since they + * can already provide more accurate and specific tool-tip content. */ if (but_label.strinfo && !STRPREFIX(but->drawstr, but_label.strinfo) && !but->tip_func) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_HEADER, - .color_id = UI_TIP_LC_NORMAL, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Normal); + field->text = BLI_strdup(but_label.strinfo); } /* Tip */ if (but_tip.strinfo) { { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_HEADER, - .color_id = UI_TIP_LC_NORMAL, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Normal); if (enum_label.strinfo) { field->text = BLI_sprintfN("%s: ", but_tip.strinfo); field->text_suffix = BLI_strdup(enum_label.strinfo); @@ -823,59 +807,40 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, /* special case enum rna buttons */ if ((but->type & UI_BTYPE_ROW) && rnaprop && RNA_property_flag(rnaprop) & PROP_ENUM_FLAG) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_NORMAL, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal); field->text = BLI_strdup(TIP_("(Shift-Click/Drag to select multiple)")); } } - /* Enum field label & tip */ + /* Enum field label & tip. */ if (enum_tip.strinfo) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value); field->text = BLI_strdup(enum_tip.strinfo); } - /* Op shortcut */ + /* Operator shortcut. */ if (op_keymap.strinfo) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_sprintfN(TIP_("Shortcut: %s"), op_keymap.strinfo); } - /* Property context-toggle shortcut */ + /* Property context-toggle shortcut. */ if (prop_keymap.strinfo) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_sprintfN(TIP_("Shortcut: %s"), prop_keymap.strinfo); } if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - /* better not show the value of a password */ + /* Better not show the value of a password. */ if ((rnaprop && (RNA_property_subtype(rnaprop) == PROP_PASSWORD)) == 0) { - /* full string */ + /* Full string. */ ui_but_string_get(but, buf, sizeof(buf)); if (buf[0]) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_sprintfN(TIP_("Value: %s"), buf); } } @@ -890,22 +855,16 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, RNA_property_float_get_index(&but->rnapoin, rnaprop, but->rnaindex) : RNA_property_float_get(&but->rnapoin, rnaprop); - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value); field->text = BLI_sprintfN(TIP_("Radians: %f"), value); } } if (but->flag & UI_BUT_DRIVEN) { if (ui_but_anim_expression_get(but, buf, sizeof(buf))) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_NORMAL, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal); field->text = BLI_sprintfN(TIP_("Expression: %s"), buf); } } @@ -913,64 +872,56 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, if (but->rnapoin.owner_id) { const ID *id = but->rnapoin.owner_id; if (ID_IS_LINKED(id)) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_NORMAL, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal); field->text = BLI_sprintfN(TIP_("Library: %s"), id->lib->filepath); } } } else if (optype) { PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) : - /* allocated when needed, the button owns it */ + /* Allocated when needed, the button owns it. */ UI_but_operator_ptr_get(but); - /* so the context is passed to fieldf functions (some py fieldf functions use it) */ + /* So the context is passed to field functions (some Python field functions use it). */ WM_operator_properties_sanitize(opptr, false); char *str = ui_tooltip_text_python_from_op(C, optype, opptr); - /* operator info */ + /* Operator info. */ if (U.flag & USER_TOOLTIPS_PYTHON) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_MONO, - .color_id = UI_TIP_LC_PYTHON, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Mono, uiTooltipFormat::ColorID::Python, true); field->text = BLI_sprintfN(TIP_("Python: %s"), str); } MEM_freeN(str); } - /* button is disabled, we may be able to tell user why */ + /* Button is disabled, we may be able to tell user why. */ if ((but->flag & UI_BUT_DISABLED) || extra_icon) { - const char *disabled_msg = NULL; + const char *disabled_msg = nullptr; bool disabled_msg_free = false; - /* if operator poll check failed, it can give pretty precise info why */ + /* If operator poll check failed, it can give pretty precise info why. */ if (optype) { const wmOperatorCallContext opcontext = extra_icon ? extra_icon->optype_params->opcontext : but->opcontext; + wmOperatorCallParams call_params{}; + call_params.optype = optype; + call_params.opcontext = opcontext; CTX_wm_operator_poll_msg_clear(C); - ui_but_context_poll_operator_ex( - C, but, &(wmOperatorCallParams){.optype = optype, .opcontext = opcontext}); + ui_but_context_poll_operator_ex(C, but, &call_params); disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free); } - /* alternatively, buttons can store some reasoning too */ + /* Alternatively, buttons can store some reasoning too. */ else if (!extra_icon && but->disabled_info) { disabled_msg = TIP_(but->disabled_info); } if (disabled_msg && disabled_msg[0]) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_ALERT, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Alert); field->text = BLI_sprintfN(TIP_("Disabled: %s"), disabled_msg); } if (disabled_msg_free) { @@ -980,30 +931,23 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, if ((U.flag & USER_TOOLTIPS_PYTHON) && !optype && rna_struct.strinfo) { { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_MONO, - .color_id = UI_TIP_LC_PYTHON, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Mono, uiTooltipFormat::ColorID::Python, true); if (rna_prop.strinfo) { /* Struct and prop */ field->text = BLI_sprintfN(TIP_("Python: %s.%s"), rna_struct.strinfo, rna_prop.strinfo); } else { - /* Only struct (e.g. menus) */ + /* Only struct (e.g. menus). */ field->text = BLI_sprintfN(TIP_("Python: %s"), rna_struct.strinfo); } } if (but->rnapoin.owner_id) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_MONO, - .color_id = UI_TIP_LC_PYTHON, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Mono, uiTooltipFormat::ColorID::Python); - /* this could get its own 'BUT_GET_...' type */ + /* This could get its own `BUT_GET_...` type. */ /* never fails */ /* Move ownership (no need for re-allocation). */ @@ -1045,73 +989,66 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, if (data->fields_len == 0) { MEM_freeN(data); - return NULL; + return nullptr; } return data; } static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz) { - uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); + uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__); - /* TODO(campbell): a way for gizmos to have their own descriptions (low priority). */ + /* TODO(@campbellbarton): a way for gizmos to have their own descriptions (low priority). */ /* Operator Actions */ { const bool use_drag = gz->drag_part != -1 && gz->highlight_part != gz->drag_part; - const struct { + struct GizmoOpActions { int part; const char *prefix; - } gzop_actions[] = { + }; + GizmoOpActions gzop_actions[] = { { - .part = gz->highlight_part, - .prefix = use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Click") : NULL, + gz->highlight_part, + use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Click") : nullptr, }, { - .part = use_drag ? gz->drag_part : -1, - .prefix = use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Drag") : NULL, + use_drag ? gz->drag_part : -1, + use_drag ? CTX_TIP_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Drag") : nullptr, }, }; for (int i = 0; i < ARRAY_SIZE(gzop_actions); i++) { wmGizmoOpElem *gzop = (gzop_actions[i].part != -1) ? WM_gizmo_operator_get(gz, gzop_actions[i].part) : - NULL; - if (gzop != NULL) { + nullptr; + if (gzop != nullptr) { /* Description */ char *info = WM_operatortype_description_or_name(C, gzop->type, &gzop->ptr); - if (info != NULL) { + if (info != nullptr) { char *text = info; - if (gzop_actions[i].prefix != NULL) { + if (gzop_actions[i].prefix != nullptr) { text = BLI_sprintfN("%s: %s", gzop_actions[i].prefix, info); MEM_freeN(info); } - if (text != NULL) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_HEADER, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + if (text != nullptr) { + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Value, true); field->text = text; } } /* Shortcut */ { - IDProperty *prop = gzop->ptr.data; + IDProperty *prop = static_cast<IDProperty *>(gzop->ptr.data); char buf[128]; if (WM_key_event_operator_string( C, gzop->type->idname, WM_OP_INVOKE_DEFAULT, prop, true, buf, ARRAY_SIZE(buf))) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_sprintfN(TIP_("Shortcut: %s"), buf); } } @@ -1123,17 +1060,13 @@ static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz) if (gz->type->target_property_defs_len) { wmGizmoProperty *gz_prop_array = WM_gizmo_target_property_array(gz); for (int i = 0; i < gz->type->target_property_defs_len; i++) { - /* TODO(campbell): function callback descriptions. */ + /* TODO(@campbellbarton): function callback descriptions. */ wmGizmoProperty *gz_prop = &gz_prop_array[i]; - if (gz_prop->prop != NULL) { + if (gz_prop->prop != nullptr) { const char *info = RNA_property_ui_description(gz_prop->prop); if (info && info[0]) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_strdup(info); } } @@ -1142,7 +1075,7 @@ static uiTooltipData *ui_tooltip_data_from_gizmo(bContext *C, wmGizmo *gz) if (data->fields_len == 0) { MEM_freeN(data); - return NULL; + return nullptr; } return data; } @@ -1161,7 +1094,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, rcti rect_i; int font_flag = 0; - /* create area region */ + /* Create area region. */ ARegion *region = ui_region_temp_add(CTX_wm_screen(C)); static ARegionType type; @@ -1171,7 +1104,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, type.regionid = RGN_TYPE_TEMPORARY; region->type = &type; - /* set font, get bb */ + /* Set font, get bounding-box. */ data->fstyle = style->widget; /* copy struct */ ui_fontscale(&data->fstyle.points, aspect); @@ -1185,7 +1118,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, BLF_wordwrap(data->fstyle.uifont_id, data->wrap_width); BLF_wordwrap(blf_mono_font, data->wrap_width); - /* these defines tweaked depending on font */ + /* These defines tweaked depending on font. */ #define TIP_BORDER_X (16.0f / aspect) #define TIP_BORDER_Y (6.0f / aspect) @@ -1194,18 +1127,19 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, int i, fonth, fontw; for (i = 0, fontw = 0, fonth = 0; i < data->fields_len; i++) { uiTooltipField *field = &data->fields[i]; - uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : NULL; + uiTooltipField *field_next = (i + 1) != data->fields_len ? &data->fields[i + 1] : nullptr; struct ResultBLF info; int w, x_pos = 0; int font_id; - if (field->format.style == UI_TIP_STYLE_MONO) { + if (field->format.style == uiTooltipFormat::Style::Mono) { BLF_size(blf_mono_font, data->fstyle.points * U.pixelsize, U.dpi); font_id = blf_mono_font; } else { - BLI_assert(ELEM(field->format.style, UI_TIP_STYLE_NORMAL, UI_TIP_STYLE_HEADER)); + BLI_assert(ELEM( + field->format.style, uiTooltipFormat::Style::Normal, uiTooltipFormat::Style::Header)); font_id = data->fstyle.uifont_id; } w = BLF_width_ex(font_id, field->text, UI_TIP_STR_MAX, &info); @@ -1253,22 +1187,20 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, /* Clamp to window bounds. */ { - /* Ensure at least 5 px above screen bounds - * UI_UNIT_Y is just a guess to be above the menu item */ - if (init_rect_overlap != NULL) { + /* Ensure at least 5 px above screen bounds. + * #UI_UNIT_Y is just a guess to be above the menu item. */ + if (init_rect_overlap != nullptr) { const int pad = max_ff(1.0f, U.pixelsize) * 5; - const rcti init_rect = { - .xmin = init_rect_overlap->xmin - pad, - .xmax = init_rect_overlap->xmax + pad, - .ymin = init_rect_overlap->ymin - pad, - .ymax = init_rect_overlap->ymax + pad, - }; - const rcti rect_clamp = { - .xmin = 0, - .xmax = winx, - .ymin = 0, - .ymax = winy, - }; + rcti init_rect; + init_rect.xmin = init_rect_overlap->xmin - pad; + init_rect.xmax = init_rect_overlap->xmax + pad; + init_rect.ymin = init_rect_overlap->ymin - pad; + init_rect.ymax = init_rect_overlap->ymax + pad; + rcti rect_clamp; + rect_clamp.xmin = 0; + rect_clamp.xmax = winx; + rect_clamp.ymin = 0; + rect_clamp.ymax = winy; /* try right. */ const int size_x = BLI_rcti_size_x(&rect_i); const int size_y = BLI_rcti_size_y(&rect_i); @@ -1348,12 +1280,11 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, } else { const int pad = max_ff(1.0f, U.pixelsize) * 5; - const rcti rect_clamp = { - .xmin = pad, - .xmax = winx - pad, - .ymin = pad + (UI_UNIT_Y * 2), - .ymax = winy - pad, - }; + rcti rect_clamp; + rect_clamp.xmin = pad; + rect_clamp.xmax = winx - pad; + rect_clamp.ymin = pad + (UI_UNIT_Y * 2); + rect_clamp.ymax = winy - pad; int offset_dummy[2]; BLI_rcti_clamp(&rect_i, &rect_clamp, offset_dummy); } @@ -1368,7 +1299,7 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, { /* Compensate for margin offset, visually this corrects the position. */ const int margin = UI_POPUP_MARGIN; - if (init_rect_overlap != NULL) { + if (init_rect_overlap != nullptr) { BLI_rcti_translate(&rect_i, margin, margin / 2); } @@ -1403,29 +1334,29 @@ ARegion *UI_tooltip_create_from_button_or_extra_icon( bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label) { wmWindow *win = CTX_wm_window(C); - /* aspect values that shrink text are likely unreadable */ + /* Aspect values that shrink text are likely unreadable. */ const float aspect = min_ff(1.0f, but->block->aspect); float init_position[2]; if (but->drawflag & UI_BUT_NO_TOOLTIP) { - return NULL; + return nullptr; } - uiTooltipData *data = NULL; + uiTooltipData *data = nullptr; - if (data == NULL) { + if (data == nullptr) { data = ui_tooltip_data_from_tool(C, but, is_label); } - if (data == NULL) { + if (data == nullptr) { data = ui_tooltip_data_from_button_or_extra_icon(C, but, extra_icon); } - if (data == NULL) { - data = ui_tooltip_data_from_button_or_extra_icon(C, but, NULL); + if (data == nullptr) { + data = ui_tooltip_data_from_button_or_extra_icon(C, but, nullptr); } - if (data == NULL) { - return NULL; + if (data == nullptr) { + return nullptr; } const bool is_no_overlap = UI_but_has_tooltip_label(but) || UI_but_is_tool(but); @@ -1454,31 +1385,30 @@ ARegion *UI_tooltip_create_from_button_or_extra_icon( } ARegion *region = ui_tooltip_create_with_data( - C, data, init_position, is_no_overlap ? &init_rect : NULL, aspect); + C, data, init_position, is_no_overlap ? &init_rect : nullptr, aspect); return region; } ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label) { - return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, NULL, is_label); + return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, nullptr, is_label); } ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) { wmWindow *win = CTX_wm_window(C); const float aspect = 1.0f; - float init_position[2] = {win->eventstate->xy[0], win->eventstate->xy[1]}; + float init_position[2] = {static_cast<float>(win->eventstate->xy[0]), + static_cast<float>(win->eventstate->xy[1])}; uiTooltipData *data = ui_tooltip_data_from_gizmo(C, gz); - if (data == NULL) { - return NULL; + if (data == nullptr) { + return nullptr; } - /* TODO(harley): - * Julian preferred that the gizmo callback return the 3D bounding box - * which we then project to 2D here. Would make a nice improvement. - */ + /* TODO(@harley): Julian preferred that the gizmo callback return the 3D bounding box + * which we then project to 2D here. Would make a nice improvement. */ if (gz->type->screen_bounds_get) { rcti bounds; if (gz->type->screen_bounds_get(C, gz, &bounds)) { @@ -1487,46 +1417,34 @@ ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) } } - return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect); + return ui_tooltip_create_with_data(C, data, init_position, nullptr, aspect); } static uiTooltipData *ui_tooltip_data_from_search_item_tooltip_data( const uiSearchItemTooltipData *item_tooltip_data) { - uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); + uiTooltipData *data = MEM_cnew<uiTooltipData>(__func__); if (item_tooltip_data->description[0]) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_HEADER, - .color_id = UI_TIP_LC_NORMAL, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Header, uiTooltipFormat::ColorID::Normal, true); field->text = BLI_strdup(item_tooltip_data->description); } if (item_tooltip_data->name && item_tooltip_data->name[0]) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_VALUE, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Value, true); field->text = BLI_strdup(item_tooltip_data->name); } if (item_tooltip_data->hint[0]) { - uiTooltipField *field = text_field_add(data, - &(uiTooltipFormat){ - .style = UI_TIP_STYLE_NORMAL, - .color_id = UI_TIP_LC_NORMAL, - .is_pad = true, - }); + uiTooltipField *field = text_field_add( + data, uiTooltipFormat::Style::Normal, uiTooltipFormat::ColorID::Normal, true); field->text = BLI_strdup(item_tooltip_data->hint); } if (data->fields_len == 0) { MEM_freeN(data); - return NULL; + return nullptr; } return data; } @@ -1538,8 +1456,8 @@ ARegion *UI_tooltip_create_from_search_item_generic( const uiSearchItemTooltipData *item_tooltip_data) { uiTooltipData *data = ui_tooltip_data_from_search_item_tooltip_data(item_tooltip_data); - if (data == NULL) { - return NULL; + if (data == nullptr) { + return nullptr; } const float aspect = 1.0f; @@ -1548,7 +1466,7 @@ ARegion *UI_tooltip_create_from_search_item_generic( init_position[0] = win->eventstate->xy[0]; init_position[1] = item_rect->ymin + searchbox_region->winrct.ymin - (UI_POPUP_MARGIN / 2); - return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect); + return ui_tooltip_create_with_data(C, data, init_position, nullptr, aspect); } void UI_tooltip_free(bContext *C, bScreen *screen, ARegion *region) diff --git a/source/blender/editors/interface/interface_regions.cc b/source/blender/editors/interface/interface_regions.cc index 1a2c1f7919c..1770805cf59 100644 --- a/source/blender/editors/interface/interface_regions.cc +++ b/source/blender/editors/interface/interface_regions.cc @@ -21,7 +21,7 @@ #include "ED_screen.h" -#include "interface_regions_intern.h" +#include "interface_regions_intern.hh" ARegion *ui_region_temp_add(bScreen *screen) { diff --git a/source/blender/editors/interface/interface_regions_intern.h b/source/blender/editors/interface/interface_regions_intern.hh index 2ed2cb3d68b..6287a031f5c 100644 --- a/source/blender/editors/interface/interface_regions_intern.h +++ b/source/blender/editors/interface/interface_regions_intern.hh @@ -3,23 +3,16 @@ /** \file * \ingroup edinterface * - * Share between interface_region_*.c files. + * Share between interface_region_*.cc files. */ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - -/* interface_region_menu_popup.c */ +/* interface_region_menu_popup.cc */ uint ui_popup_menu_hash(const char *str); -/* interface_regions_intern.h */ +/* interface_regions.cc */ + ARegion *ui_region_temp_add(bScreen *screen); void ui_region_temp_remove(struct bContext *C, bScreen *screen, ARegion *region); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index 8588c7cabc0..3147deb5ad1 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -66,7 +66,7 @@ static void asset_view_item_but_drag_set(uiBut *but, } static void asset_view_draw_item(uiList *ui_list, - bContext *UNUSED(C), + const bContext *UNUSED(C), uiLayout *layout, PointerRNA *UNUSED(dataptr), PointerRNA *itemptr, @@ -183,7 +183,7 @@ static void asset_view_template_refresh_asset_collection( } void uiTemplateAssetView(uiLayout *layout, - bContext *C, + const bContext *C, const char *list_id, PointerRNA *asset_library_dataptr, const char *asset_library_propname, diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index e0b6bbb34c4..f0c91588f98 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -83,7 +83,7 @@ struct TemplateListVisualInfo { }; static void uilist_draw_item_default(struct uiList *ui_list, - struct bContext *UNUSED(C), + const struct bContext *UNUSED(C), struct uiLayout *layout, struct PointerRNA *UNUSED(dataptr), struct PointerRNA *itemptr, @@ -114,7 +114,7 @@ static void uilist_draw_item_default(struct uiList *ui_list, } static void uilist_draw_filter_default(struct uiList *ui_list, - struct bContext *UNUSED(C), + const struct bContext *UNUSED(C), struct uiLayout *layout) { PointerRNA listptr; @@ -160,7 +160,7 @@ static int cmpstringp(const void *p1, const void *p2) } static void uilist_filter_items_default(struct uiList *ui_list, - struct bContext *UNUSED(C), + const struct bContext *UNUSED(C), struct PointerRNA *dataptr, const char *propname) { @@ -434,7 +434,7 @@ static void ui_template_list_collect_items(PointerRNA *list_ptr, /** * Create the UI-list representation of the list items, sorted and filtered if needed. */ -static void ui_template_list_collect_display_items(bContext *C, +static void ui_template_list_collect_display_items(const bContext *C, uiList *ui_list, TemplateListInputData *input_data, const uiListFilterItemsFunc filter_items_fn, @@ -601,7 +601,7 @@ static char *uilist_item_tooltip_func(bContext *UNUSED(C), void *argN, const cha /** * \note that \a layout_type may be null. */ -static uiList *ui_list_ensure(bContext *C, +static uiList *ui_list_ensure(const bContext *C, uiListType *ui_list_type, const char *list_id, int layout_type, @@ -656,7 +656,7 @@ static uiList *ui_list_ensure(bContext *C, return ui_list; } -static void ui_template_list_layout_draw(bContext *C, +static void ui_template_list_layout_draw(const bContext *C, uiList *ui_list, uiLayout *layout, TemplateListInputData *input_data, @@ -1156,7 +1156,7 @@ static void ui_template_list_layout_draw(bContext *C, } uiList *uiTemplateList_ex(uiLayout *layout, - bContext *C, + const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, @@ -1227,7 +1227,7 @@ uiList *uiTemplateList_ex(uiLayout *layout, } void uiTemplateList(uiLayout *layout, - bContext *C, + const bContext *C, const char *listtype_name, const char *list_id, PointerRNA *dataptr, diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.cc index 41de2ab197d..0d0a5f01744 100644 --- a/source/blender/editors/interface/interface_template_search_operator.c +++ b/source/blender/editors/interface/interface_template_search_operator.cc @@ -7,14 +7,15 @@ * accessed via the #WM_OT_search_operator operator. */ -#include <string.h> +#include <cstring> #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_texture_types.h" -#include "BLI_alloca.h" +#include "BLI_array.hh" #include "BLI_ghash.h" +#include "BLI_math_vec_types.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -35,10 +36,10 @@ static void operator_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) { - wmOperatorType *ot = arg2; + wmOperatorType *ot = static_cast<wmOperatorType *>(arg2); if (ot) { - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, NULL, NULL); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, nullptr, nullptr); } } @@ -53,19 +54,20 @@ static void operator_search_update_fn(const bContext *C, /* Prepare BLI_string_all_words_matched. */ const size_t str_len = strlen(str); const int words_max = BLI_string_max_possible_word_count(str_len); - int(*words)[2] = BLI_array_alloca(words, words_max); - const int words_len = BLI_string_find_split_words(str, str_len, ' ', words, words_max); + blender::Array<blender::int2> words(words_max); + const int words_len = BLI_string_find_split_words( + str, str_len, ' ', (int(*)[2])words.data(), words_max); for (WM_operatortype_iter(&iter); !BLI_ghashIterator_done(&iter); BLI_ghashIterator_step(&iter)) { - wmOperatorType *ot = BLI_ghashIterator_getValue(&iter); + wmOperatorType *ot = static_cast<wmOperatorType *>(BLI_ghashIterator_getValue(&iter)); const char *ot_ui_name = CTX_IFACE_(ot->translation_context, ot->name); if ((ot->flag & OPTYPE_INTERNAL) && (G.debug & G_DEBUG_WM) == 0) { continue; } - if (BLI_string_all_words_matched(ot_ui_name, str, words, words_len)) { + if (BLI_string_all_words_matched(ot_ui_name, str, (int(*)[2])words.data(), words_len)) { if (WM_operator_poll((bContext *)C, ot)) { char name[256]; const int len = strlen(ot_ui_name); @@ -78,7 +80,7 @@ static void operator_search_update_fn(const bContext *C, if (WM_key_event_operator_string(C, ot->idname, WM_OP_EXEC_DEFAULT, - NULL, + nullptr, true, &name[len + 1], sizeof(name) - len - 1)) { @@ -105,11 +107,11 @@ void UI_but_func_operator_search(uiBut *but) UI_but_func_search_set(but, ui_searchbox_create_operator, operator_search_update_fn, - NULL, + nullptr, false, - NULL, + nullptr, operator_search_exec_fn, - NULL); + nullptr); } void uiTemplateOperatorSearch(uiLayout *layout) diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index b131bf0adfe..b4f19c6ec83 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -6702,7 +6702,7 @@ void uiTemplateCacheFileTimeSettings(uiLayout *layout, PointerRNA *fileptr) } static void cache_file_layer_item(uiList *UNUSED(ui_list), - bContext *UNUSED(C), + const bContext *UNUSED(C), uiLayout *layout, PointerRNA *UNUSED(dataptr), PointerRNA *itemptr, diff --git a/source/blender/editors/interface/interface_undo.c b/source/blender/editors/interface/interface_undo.cc index e998eb6dbed..ec54b695cf7 100644 --- a/source/blender/editors/interface/interface_undo.c +++ b/source/blender/editors/interface/interface_undo.cc @@ -7,7 +7,7 @@ * Undo stack to use for UI widgets that manage their own editing state. */ -#include <string.h> +#include <cstring> #include "BLI_listbase.h" @@ -21,39 +21,39 @@ /** \name Text Field Undo Stack * \{ */ -typedef struct uiUndoStack_Text_State { +struct uiUndoStack_Text_State { struct uiUndoStack_Text_State *next, *prev; int cursor_index; char text[0]; -} uiUndoStack_Text_State; +}; -typedef struct uiUndoStack_Text { +struct uiUndoStack_Text { ListBase states; uiUndoStack_Text_State *current; -} uiUndoStack_Text; +}; static const char *ui_textedit_undo_impl(uiUndoStack_Text *stack, int *r_cursor_index) { /* Don't undo if no data has been pushed yet. */ - if (stack->current == NULL) { - return NULL; + if (stack->current == nullptr) { + return nullptr; } /* Travel backwards in the stack and copy information to the caller. */ - if (stack->current->prev != NULL) { + if (stack->current->prev != nullptr) { stack->current = stack->current->prev; *r_cursor_index = stack->current->cursor_index; return stack->current->text; } - return NULL; + return nullptr; } static const char *ui_textedit_redo_impl(uiUndoStack_Text *stack, int *r_cursor_index) { /* Don't redo if no data has been pushed yet. */ - if (stack->current == NULL) { - return NULL; + if (stack->current == nullptr) { + return nullptr; } /* Only redo if new data has not been entered since the last undo. */ @@ -63,7 +63,7 @@ static const char *ui_textedit_redo_impl(uiUndoStack_Text *stack, int *r_cursor_ *r_cursor_index = stack->current->cursor_index; return stack->current->text; } - return NULL; + return nullptr; } const char *ui_textedit_undo(uiUndoStack_Text *stack, int direction, int *r_cursor_index) @@ -78,7 +78,7 @@ const char *ui_textedit_undo(uiUndoStack_Text *stack, int direction, int *r_curs void ui_textedit_undo_push(uiUndoStack_Text *stack, const char *text, int cursor_index) { /* Clear all redo actions from the current state. */ - if (stack->current != NULL) { + if (stack->current != nullptr) { while (stack->current->next) { uiUndoStack_Text_State *state = stack->current->next; BLI_remlink(&stack->states, state); @@ -88,7 +88,8 @@ void ui_textedit_undo_push(uiUndoStack_Text *stack, const char *text, int cursor /* Create the new state. */ const int text_size = strlen(text) + 1; - stack->current = MEM_mallocN(sizeof(uiUndoStack_Text_State) + text_size, __func__); + stack->current = static_cast<uiUndoStack_Text_State *>( + MEM_mallocN(sizeof(uiUndoStack_Text_State) + text_size, __func__)); stack->current->cursor_index = cursor_index; memcpy(stack->current->text, text, text_size); BLI_addtail(&stack->states, stack->current); @@ -96,8 +97,8 @@ void ui_textedit_undo_push(uiUndoStack_Text *stack, const char *text, int cursor uiUndoStack_Text *ui_textedit_undo_stack_create(void) { - uiUndoStack_Text *stack = MEM_mallocN(sizeof(uiUndoStack_Text), __func__); - stack->current = NULL; + uiUndoStack_Text *stack = MEM_new<uiUndoStack_Text>(__func__); + stack->current = nullptr; BLI_listbase_clear(&stack->states); return stack; diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index a716c00d5d9..568ece00c4c 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -11,9 +11,9 @@ set(INC ../../io/collada ../../io/common ../../io/gpencil + ../../io/stl ../../io/usd ../../io/wavefront_obj - ../../io/stl ../../makesdna ../../makesrna ../../windowmanager @@ -33,8 +33,8 @@ set(SRC io_gpencil_utils.c io_obj.c io_ops.c - io_usd.c io_stl_ops.c + io_usd.c io_alembic.h io_cache.h @@ -42,8 +42,8 @@ set(SRC io_gpencil.h io_obj.h io_ops.h - io_usd.h io_stl_ops.h + io_usd.h ) set(LIB diff --git a/source/blender/editors/io/io_gpencil_import.c b/source/blender/editors/io/io_gpencil_import.c index 9ac64407dcf..b6fecfaf94e 100644 --- a/source/blender/editors/io/io_gpencil_import.c +++ b/source/blender/editors/io/io_gpencil_import.c @@ -9,6 +9,8 @@ # include "BLI_path_util.h" +# include "MEM_guardedalloc.h" + # include "DNA_gpencil_types.h" # include "DNA_space_types.h" @@ -63,7 +65,8 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - if (!RNA_struct_property_is_set(op->ptr, "filepath")) { + if (!RNA_struct_property_is_set(op->ptr, "filepath") || + !(RNA_struct_find_property(op->ptr, "directory"))) { BKE_report(op->reports, RPT_ERROR, "No filename given"); return OPERATOR_CANCELLED; } @@ -75,9 +78,6 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op) } View3D *v3d = get_invoke_view3d(C); - char filename[FILE_MAX]; - RNA_string_get(op->ptr, "filepath", filename); - /* Set flags. */ int flag = 0; @@ -101,13 +101,31 @@ static int wm_gpencil_import_svg_exec(bContext *C, wmOperator *op) .resolution = resolution, }; - /* Do Import. */ - WM_cursor_wait(1); - const bool done = gpencil_io_import(filename, ¶ms); - WM_cursor_wait(0); - - if (!done) { - BKE_report(op->reports, RPT_WARNING, "Unable to import SVG"); + /* Loop all selected files to import them. All SVG imported shared the same import + * parameters, but they are created in separated grease pencil objects. */ + PropertyRNA *prop; + if ((prop = RNA_struct_find_property(op->ptr, "directory"))) { + char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL); + + if ((prop = RNA_struct_find_property(op->ptr, "files"))) { + char file_path[FILE_MAX]; + RNA_PROP_BEGIN (op->ptr, itemptr, prop) { + char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0, NULL); + BLI_join_dirfile(file_path, sizeof(file_path), directory, filename); + MEM_freeN(filename); + + /* Do Import. */ + WM_cursor_wait(1); + RNA_string_get(&itemptr, "name", params.filename); + const bool done = gpencil_io_import(file_path, ¶ms); + WM_cursor_wait(0); + if (!done) { + BKE_reportf(op->reports, RPT_WARNING, "Unable to import '%s'", file_path); + } + } + RNA_PROP_END; + } + MEM_freeN(directory); } return OPERATOR_FINISHED; @@ -149,10 +167,11 @@ void WM_OT_gpencil_import_svg(wmOperatorType *ot) ot->check = wm_gpencil_import_svg_common_check; WM_operator_properties_filesel(ot, - FILE_TYPE_OBJECT_IO, + FILE_TYPE_FOLDER | FILE_TYPE_OBJECT_IO, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH | WM_FILESEL_SHOW_PROPS | + WM_FILESEL_DIRECTORY | WM_FILESEL_FILES, FILE_DEFAULTDISPLAY, FILE_SORT_DEFAULT); diff --git a/source/blender/editors/io/io_obj.c b/source/blender/editors/io/io_obj.c index 662ff601e29..c151baf13ef 100644 --- a/source/blender/editors/io/io_obj.c +++ b/source/blender/editors/io/io_obj.c @@ -382,11 +382,6 @@ static int wm_obj_import_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS static int wm_obj_import_exec(bContext *C, wmOperator *op) { - if (!RNA_struct_property_is_set(op->ptr, "filepath")) { - BKE_report(op->reports, RPT_ERROR, "No filename given"); - return OPERATOR_CANCELLED; - } - struct OBJImportParams import_params; RNA_string_get(op->ptr, "filepath", import_params.filepath); import_params.clamp_size = RNA_float_get(op->ptr, "clamp_size"); @@ -395,8 +390,35 @@ static int wm_obj_import_exec(bContext *C, wmOperator *op) import_params.import_vertex_groups = RNA_boolean_get(op->ptr, "import_vertex_groups"); import_params.validate_meshes = RNA_boolean_get(op->ptr, "validate_meshes"); import_params.relative_paths = ((U.flag & USER_RELPATHS) != 0); - - OBJ_import(C, &import_params); + import_params.clear_selection = true; + + int files_len = RNA_collection_length(op->ptr, "files"); + if (files_len) { + /* Importing multiple files: loop over them and import one by one. */ + PointerRNA fileptr; + PropertyRNA *prop; + char dir_only[FILE_MAX], file_only[FILE_MAX]; + + RNA_string_get(op->ptr, "directory", dir_only); + prop = RNA_struct_find_property(op->ptr, "files"); + for (int i = 0; i < files_len; i++) { + RNA_property_collection_lookup_int(op->ptr, prop, i, &fileptr); + RNA_string_get(&fileptr, "name", file_only); + BLI_join_dirfile( + import_params.filepath, sizeof(import_params.filepath), dir_only, file_only); + import_params.clear_selection = (i == 0); + OBJ_import(C, &import_params); + } + } + else if (RNA_struct_property_is_set(op->ptr, "filepath")) { + /* Importing one file. */ + RNA_string_get(op->ptr, "filepath", import_params.filepath); + OBJ_import(C, &import_params); + } + else { + BKE_report(op->reports, RPT_ERROR, "No filename given"); + return OPERATOR_CANCELLED; + } Scene *scene = CTX_data_scene(C); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); @@ -454,7 +476,8 @@ void WM_OT_obj_import(struct wmOperatorType *ot) FILE_TYPE_FOLDER, FILE_BLENDER, FILE_OPENFILE, - WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS, + WM_FILESEL_FILEPATH | WM_FILESEL_SHOW_PROPS | + WM_FILESEL_DIRECTORY | WM_FILESEL_FILES, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); RNA_def_float( diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c index 7615a57c8fe..8265225e08a 100644 --- a/source/blender/editors/lattice/editlattice_undo.c +++ b/source/blender/editors/lattice/editlattice_undo.c @@ -46,7 +46,7 @@ static CLG_LogRef LOG = {"ed.undo.lattice"}; /** \name Undo Conversion * \{ */ -/* TODO(Campbell): this could contain an entire 'Lattice' struct. */ +/* TODO(@campbellbarton): this could contain an entire 'Lattice' struct. */ typedef struct UndoLattice { BPoint *def; int pntsu, pntsv, pntsw, actbp; diff --git a/source/blender/editors/mesh/editface.cc b/source/blender/editors/mesh/editface.cc index b69cd8b8606..c55d96bac55 100644 --- a/source/blender/editors/mesh/editface.cc +++ b/source/blender/editors/mesh/editface.cc @@ -17,6 +17,7 @@ #include "DNA_meshdata_types.h" #include "DNA_object_types.h" +#include "BKE_attribute.hh" #include "BKE_context.h" #include "BKE_customdata.h" #include "BKE_global.h" @@ -36,14 +37,18 @@ /* own include */ -void paintface_flush_flags(bContext *C, Object *ob, short flag) +void paintface_flush_flags(bContext *C, + Object *ob, + const bool flush_selection, + const bool flush_hidden) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); MPoly *polys, *mp_orig; const int *index_array = nullptr; int totpoly; - BLI_assert((flag & ~(SELECT | ME_HIDE)) == 0); + BLI_assert(flush_selection || flush_hidden); if (me == nullptr) { return; @@ -53,7 +58,7 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) /* we could call this directly in all areas that change selection, * since this could become slow for realtime updates (circle-select for eg) */ - if (flag & SELECT) { + if (flush_selection) { BKE_mesh_flush_select_from_polys(me); } @@ -64,8 +69,11 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) return; } + bke::AttributeAccessor attributes_me = bke::mesh_attributes(*me); Mesh *me_orig = (Mesh *)ob_eval->runtime.data_orig; + bke::MutableAttributeAccessor attributes_orig = bke::mesh_attributes_for_write(*me_orig); Mesh *me_eval = (Mesh *)ob_eval->runtime.data_eval; + bke::MutableAttributeAccessor attributes_eval = bke::mesh_attributes_for_write(*me_eval); bool updated = false; if (me_orig != nullptr && me_eval != nullptr && me_orig->totpoly == me->totpoly) { @@ -73,13 +81,17 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) for (int i = 0; i < me->totpoly; i++) { me_orig->mpoly[i].flag = me->mpoly[i].flag; } - - /* If the mesh has only deform modifiers, the evaluated mesh shares arrays. */ - if (me_eval->mpoly == me_orig->mpoly) { - updated = true; + if (flush_hidden) { + const VArray<bool> hide_poly_me = attributes_me.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + bke::SpanAttributeWriter<bool> hide_poly_orig = + attributes_orig.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE); + hide_poly_me.materialize(hide_poly_orig.span); + hide_poly_orig.finish(); } + /* Mesh polys => Final derived polys */ - else if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { + if ((index_array = (const int *)CustomData_get_layer(&me_eval->pdata, CD_ORIGINDEX))) { polys = me_eval->mpoly; totpoly = me_eval->totpoly; @@ -91,13 +103,24 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) polys[i].flag = mp_orig->flag; } } + const VArray<bool> hide_poly_orig = attributes_orig.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + bke::SpanAttributeWriter<bool> hide_poly_eval = + attributes_eval.lookup_or_add_for_write_only_span<bool>(".hide_poly", ATTR_DOMAIN_FACE); + for (const int i : IndexRange(me_eval->totpoly)) { + const int orig_poly_index = index_array[i]; + if (orig_poly_index != ORIGINDEX_NONE) { + hide_poly_eval.span[i] = hide_poly_orig[orig_poly_index]; + } + } + hide_poly_eval.finish(); updated = true; } } if (updated) { - if (flag & ME_HIDE) { + if (flush_hidden) { BKE_mesh_batch_cache_dirty_tag(me_eval, BKE_MESH_BATCH_DIRTY_ALL); } else { @@ -115,59 +138,79 @@ void paintface_flush_flags(bContext *C, Object *ob, short flag) void paintface_hide(bContext *C, Object *ob, const bool unselected) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr || me->totpoly == 0) { return; } + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>( + ".hide_poly", ATTR_DOMAIN_FACE); + for (int i = 0; i < me->totpoly; i++) { MPoly *mpoly = &me->mpoly[i]; - if ((mpoly->flag & ME_HIDE) == 0) { + if (!hide_poly.span[i]) { if (((mpoly->flag & ME_FACE_SEL) == 0) == unselected) { - mpoly->flag |= ME_HIDE; + hide_poly.span[i] = true; } } - if (mpoly->flag & ME_HIDE) { + if (hide_poly.span[i]) { mpoly->flag &= ~ME_FACE_SEL; } } + hide_poly.finish(); + BKE_mesh_flush_hidden_from_polys(me); - paintface_flush_flags(C, ob, SELECT | ME_HIDE); + paintface_flush_flags(C, ob, true, true); } void paintface_reveal(bContext *C, Object *ob, const bool select) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr || me->totpoly == 0) { return; } - for (int i = 0; i < me->totpoly; i++) { - MPoly *mpoly = &me->mpoly[i]; - if (mpoly->flag & ME_HIDE) { - SET_FLAG_FROM_TEST(mpoly->flag, select, ME_FACE_SEL); - mpoly->flag &= ~ME_HIDE; + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + + if (select) { + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + for (int i = 0; i < me->totpoly; i++) { + MPoly *mpoly = &me->mpoly[i]; + if (hide_poly[i]) { + mpoly->flag |= ME_FACE_SEL; + } } } + attributes.remove(".hide_poly"); + BKE_mesh_flush_hidden_from_polys(me); - paintface_flush_flags(C, ob, SELECT | ME_HIDE); + paintface_flush_flags(C, ob, true, true); } /* Set object-mode face selection seams based on edge data, uses hash table to find seam edges. */ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bool select) { + using namespace blender; bool do_it = true; bool mark = false; BLI_bitmap *edge_tag = BLI_BITMAP_NEW(me->totedge, __func__); BLI_bitmap *poly_tag = BLI_BITMAP_NEW(me->totpoly, __func__); + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (index != (uint)-1) { /* only put face under cursor in array */ MPoly *mp = &me->mpoly[index]; @@ -178,7 +221,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo /* fill array by selection */ for (int i = 0; i < me->totpoly; i++) { MPoly *mp = &me->mpoly[i]; - if (mp->flag & ME_HIDE) { + if (hide_poly[i]) { /* pass */ } else if (mp->flag & ME_FACE_SEL) { @@ -194,7 +237,7 @@ static void select_linked_tfaces_with_seams(Mesh *me, const uint index, const bo /* expand selection */ for (int i = 0; i < me->totpoly; i++) { MPoly *mp = &me->mpoly[i]; - if (mp->flag & ME_HIDE) { + if (hide_poly[i]) { continue; } @@ -249,22 +292,27 @@ void paintface_select_linked(bContext *C, Object *ob, const int mval[2], const b select_linked_tfaces_with_seams(me, index, select); - paintface_flush_flags(C, ob, SELECT); + paintface_flush_flags(C, ob, true, false); } bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr) { return false; } + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (action == SEL_TOGGLE) { action = SEL_SELECT; for (int i = 0; i < me->totpoly; i++) { MPoly *mpoly = &me->mpoly[i]; - if ((mpoly->flag & ME_HIDE) == 0 && mpoly->flag & ME_FACE_SEL) { + if (!hide_poly[i] && mpoly->flag & ME_FACE_SEL) { action = SEL_DESELECT; break; } @@ -275,7 +323,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl for (int i = 0; i < me->totpoly; i++) { MPoly *mpoly = &me->mpoly[i]; - if ((mpoly->flag & ME_HIDE) == 0) { + if (!hide_poly[i]) { switch (action) { case SEL_SELECT: if ((mpoly->flag & ME_FACE_SEL) == 0) { @@ -299,7 +347,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl if (changed) { if (flush_flags) { - paintface_flush_flags(C, ob, SELECT); + paintface_flush_flags(C, ob, true, false); } } return changed; @@ -307,6 +355,7 @@ bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool fl bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) { + using namespace blender; bool ok = false; float vec[3], bmat[3][3]; @@ -318,9 +367,13 @@ bool paintface_minmax(Object *ob, float r_min[3], float r_max[3]) copy_m3_m4(bmat, ob->obmat); + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + for (int i = 0; i < me->totpoly; i++) { MPoly *mp = &me->mpoly[i]; - if (mp->flag & ME_HIDE || !(mp->flag & ME_FACE_SEL)) { + if (hide_poly[i] || !(mp->flag & ME_FACE_SEL)) { continue; } @@ -342,6 +395,7 @@ bool paintface_mouse_select(bContext *C, const SelectPick_Params *params, Object *ob) { + using namespace blender; MPoly *mpoly_sel = nullptr; uint index; bool changed = false; @@ -350,10 +404,14 @@ bool paintface_mouse_select(bContext *C, /* Get the face under the cursor */ Mesh *me = BKE_mesh_from_object(ob); + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_poly = attributes.lookup_or_default<bool>( + ".hide_poly", ATTR_DOMAIN_FACE, false); + if (ED_mesh_pick_face(C, ob, mval, ED_MESH_PICK_DEFAULT_FACE_DIST, &index)) { if (index < me->totpoly) { mpoly_sel = me->mpoly + index; - if ((mpoly_sel->flag & ME_HIDE) == 0) { + if (!hide_poly[index]) { found = true; } } @@ -402,7 +460,7 @@ bool paintface_mouse_select(bContext *C, /* image window redraw */ - paintface_flush_flags(C, ob, SELECT); + paintface_flush_flags(C, ob, true, false); ED_region_tag_redraw(CTX_wm_region(C)); /* XXX: should redraw all 3D views. */ changed = true; } @@ -463,17 +521,22 @@ void paintvert_tag_select_update(bContext *C, Object *ob) bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr) { return false; } + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + if (action == SEL_TOGGLE) { action = SEL_SELECT; for (int i = 0; i < me->totvert; i++) { MVert *mvert = &me->mvert[i]; - if ((mvert->flag & ME_HIDE) == 0 && mvert->flag & SELECT) { + if (!hide_vert[i] && mvert->flag & SELECT) { action = SEL_DESELECT; break; } @@ -483,7 +546,7 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) bool changed = false; for (int i = 0; i < me->totvert; i++) { MVert *mvert = &me->mvert[i]; - if ((mvert->flag & ME_HIDE) == 0) { + if (!hide_vert[i]) { switch (action) { case SEL_SELECT: if ((mvert->flag & SELECT) == 0) { @@ -526,6 +589,7 @@ bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags) void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) { + using namespace blender; Mesh *me = BKE_mesh_from_object(ob); if (me == nullptr || me->dvert == nullptr) { @@ -536,10 +600,14 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) paintvert_deselect_all_visible(ob, SEL_DESELECT, false); } + bke::AttributeAccessor attributes = bke::mesh_attributes(*me); + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + for (int i = 0; i < me->totvert; i++) { MVert *mv = &me->mvert[i]; MDeformVert *dv = &me->dvert[i]; - if ((mv->flag & ME_HIDE) == 0) { + if (!hide_vert[i]) { if (dv->dw == nullptr) { /* if null weight then not grouped */ mv->flag |= SELECT; @@ -554,25 +622,30 @@ void paintvert_select_ungrouped(Object *ob, bool extend, bool flush_flags) void paintvert_hide(bContext *C, Object *ob, const bool unselected) { - Mesh *const me = BKE_mesh_from_object(ob); - + using namespace blender; + Mesh *me = BKE_mesh_from_object(ob); if (me == NULL || me->totvert == 0) { return; } - for (int i = 0; i < me->totvert; i++) { - MVert *const mvert = &me->mvert[i]; + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + bke::SpanAttributeWriter<bool> hide_vert = attributes.lookup_or_add_for_write_span<bool>( + ".hide_vert", ATTR_DOMAIN_POINT); + MutableSpan<MVert> verts(me->mvert, me->totvert); - if ((mvert->flag & ME_HIDE) == 0) { - if (((mvert->flag & SELECT) == 0) == unselected) { - mvert->flag |= ME_HIDE; + for (const int i : verts.index_range()) { + MVert &vert = verts[i]; + if (!hide_vert.span[i]) { + if (((vert.flag & SELECT) == 0) == unselected) { + hide_vert.span[i] = true; } } - if (mvert->flag & ME_HIDE) { - mvert->flag &= ~SELECT; + if (hide_vert.span[i]) { + vert.flag &= ~SELECT; } } + hide_vert.finish(); BKE_mesh_flush_hidden_from_verts(me); @@ -582,21 +655,27 @@ void paintvert_hide(bContext *C, Object *ob, const bool unselected) void paintvert_reveal(bContext *C, Object *ob, const bool select) { - Mesh *const me = BKE_mesh_from_object(ob); - + using namespace blender; + Mesh *me = BKE_mesh_from_object(ob); if (me == NULL || me->totvert == 0) { return; } - for (int i = 0; i < me->totvert; i++) { - MVert *const mvert = &me->mvert[i]; + bke::MutableAttributeAccessor attributes = bke::mesh_attributes_for_write(*me); + const VArray<bool> hide_vert = attributes.lookup_or_default<bool>( + ".hide_vert", ATTR_DOMAIN_POINT, false); + MutableSpan<MVert> verts(me->mvert, me->totvert); - if (mvert->flag & ME_HIDE) { - SET_FLAG_FROM_TEST(mvert->flag, select, SELECT); - mvert->flag &= ~ME_HIDE; + for (const int i : verts.index_range()) { + MVert &vert = verts[i]; + if (hide_vert[i]) { + SET_FLAG_FROM_TEST(vert.flag, select, SELECT); } } + /* Remove the hide attribute to reveal all vertices. */ + attributes.remove(".hide_vert"); + BKE_mesh_flush_hidden_from_verts(me); paintvert_flush_flags(ob); diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 5680865ae67..d4c5504615a 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -4300,7 +4300,7 @@ static void knifetool_finish_single_pre(KnifeTool_OpData *kcd, Object *ob) } /** - * A post version is needed to to delay recalculating tessellation after making cuts. + * A post version is needed to delay recalculating tessellation after making cuts. * Without this, knife-project can't use the BVH tree to select geometry after a cut, see: T98349. */ static void knifetool_finish_single_post(KnifeTool_OpData *UNUSED(kcd), Object *ob) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index c5add97fb00..7de5ad9f151 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -936,7 +936,7 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op) Object *obedit = objects[ob_index]; BMEditMesh *em = BKE_editmesh_from_object(obedit); - if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totvertsel == 0)) { + if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) { continue; } diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index d75c92f963f..af8084e16c4 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -594,6 +594,10 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo /* Uncomment for troubleshooting. */ // BM_mesh_validate(em->bm); + /* Copy the ID name characters to the mesh so code that depends on accessing the ID type can work + * on it. Necessary to use the attribute API. */ + strcpy(um->me.id.name, "MEundomesh_from_editmesh"); + BM_mesh_bm_to_me( NULL, em->bm, diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index ac5530c8ea9..e931dd02a9e 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -593,6 +593,31 @@ UvMapVert *BM_uv_vert_map_at_index(UvVertMap *vmap, uint v) return vmap->vert[v]; } +static void bm_uv_ensure_head_table(UvElementMap *element_map) +{ + if (element_map->head_table) { + return; + } + + /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */ + element_map->head_table = MEM_mallocN(sizeof(*element_map->head_table) * element_map->total_uvs, + "uv_element_map_head_table"); + UvElement **head_table = element_map->head_table; + for (int i = 0; i < element_map->total_uvs; i++) { + UvElement *head = element_map->storage + i; + if (head->separate) { + UvElement *element = head; + while (element) { + head_table[element - element_map->storage] = head; + element = element->next; + if (element && element->separate) { + break; + } + } + } + } +} + #define INVALID_ISLAND ((unsigned int)-1) static void bm_uv_assign_island(UvElementMap *element_map, @@ -603,7 +628,7 @@ static void bm_uv_assign_island(UvElementMap *element_map, int islandbufsize) { element->island = nisland; - map[element - element_map->buf] = islandbufsize; + map[element - element_map->storage] = islandbufsize; /* Copy *element to islandbuf[islandbufsize]. */ islandbuf[islandbufsize].l = element->l; @@ -620,33 +645,19 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, bool uv_selected, int cd_loop_uv_offset) { - int totuv = element_map->totalUVs; + bm_uv_ensure_head_table(element_map); - /* For each UvElement, locate the "separate" UvElement that precedes it in the linked list. */ - UvElement **head_table = MEM_mallocN(sizeof(*head_table) * totuv, "uv_island_head_table"); - for (int i = 0; i < totuv; i++) { - UvElement *head = element_map->buf + i; - if (head->separate) { - UvElement *element = head; - while (element) { - head_table[element - element_map->buf] = head; - element = element->next; - if (element && element->separate) { - break; - } - } - } - } + int total_uvs = element_map->total_uvs; /* Depth first search the graph, building islands as we go. */ int nislands = 0; int islandbufsize = 0; - int stack_upper_bound = totuv; + int stack_upper_bound = total_uvs; UvElement **stack_uv = MEM_mallocN(sizeof(*stack_uv) * stack_upper_bound, "uv_island_element_stack"); int stacksize_uv = 0; - for (int i = 0; i < totuv; i++) { - UvElement *element = element_map->buf + i; + for (int i = 0; i < total_uvs; i++) { + UvElement *element = element_map->storage + i; if (element->island != INVALID_ISLAND) { /* Unique UV (element and all it's children) are already part of an island. */ continue; @@ -676,7 +687,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, if (!uv_selected || uvedit_edge_select_test(scene, element->l, cd_loop_uv_offset)) { UvElement *next = BM_uv_element_get(element_map, element->l->next->f, element->l->next); if (next->island == INVALID_ISLAND) { - UvElement *tail = head_table[next - element_map->buf]; + UvElement *tail = element_map->head_table[next - element_map->storage]; stack_uv[stacksize_uv++] = tail; while (tail) { bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); @@ -692,7 +703,7 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, if (!uv_selected || uvedit_edge_select_test(scene, element->l->prev, cd_loop_uv_offset)) { UvElement *prev = BM_uv_element_get(element_map, element->l->prev->f, element->l->prev); if (prev->island == INVALID_ISLAND) { - UvElement *tail = head_table[prev - element_map->buf]; + UvElement *tail = element_map->head_table[prev - element_map->storage]; stack_uv[stacksize_uv++] = tail; while (tail) { bm_uv_assign_island(element_map, tail, nislands, map, islandbuf, islandbufsize++); @@ -713,14 +724,132 @@ static int bm_uv_edge_select_build_islands(UvElementMap *element_map, } nislands++; } - BLI_assert(islandbufsize == totuv); + BLI_assert(islandbufsize == total_uvs); MEM_SAFE_FREE(stack_uv); - MEM_SAFE_FREE(head_table); + MEM_SAFE_FREE(element_map->head_table); return nislands; } +static void bm_uv_build_islands(UvElementMap *element_map, + BMesh *bm, + const Scene *scene, + bool uv_selected) +{ + int totuv = element_map->total_uvs; + int nislands = 0; + int islandbufsize = 0; + + /* map holds the map from current vmap->buf to the new, sorted map */ + uint *map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap"); + BMFace **stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack"); + UvElement *islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer"); + /* Island number for BMFaces. */ + int *island_number = MEM_callocN(sizeof(*island_number) * bm->totface, "uv_island_number_face"); + copy_vn_i(island_number, bm->totface, INVALID_ISLAND); + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ? + scene->toolsettings->selectmode & SCE_SELECT_EDGE : + scene->toolsettings->uv_selectmode & UV_SELECT_EDGE; + if (use_uv_edge_connectivity) { + nislands = bm_uv_edge_select_build_islands( + element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset); + islandbufsize = totuv; + } + + for (int i = 0; i < totuv; i++) { + if (element_map->storage[i].island == INVALID_ISLAND) { + int stacksize = 0; + element_map->storage[i].island = nislands; + stack[0] = element_map->storage[i].l->f; + island_number[BM_elem_index_get(stack[0])] = nislands; + stacksize = 1; + + while (stacksize > 0) { + BMFace *efa = stack[--stacksize]; + + BMLoop *l; + BMIter liter; + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + continue; + } + + UvElement *initelement = element_map->vertex[BM_elem_index_get(l->v)]; + + for (UvElement *element = initelement; element; element = element->next) { + if (element->separate) { + initelement = element; + } + + if (element->l->f == efa) { + /* found the uv corresponding to our face and vertex. + * Now fill it to the buffer */ + bm_uv_assign_island(element_map, element, nislands, map, islandbuf, islandbufsize++); + + for (element = initelement; element; element = element->next) { + if (element->separate && element != initelement) { + break; + } + + if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) { + stack[stacksize++] = element->l->f; + island_number[BM_elem_index_get(element->l->f)] = nislands; + } + } + break; + } + } + } + } + + nislands++; + } + } + + MEM_SAFE_FREE(island_number); + + /* remap */ + for (int i = 0; i < bm->totvert; i++) { + /* important since we may do selection only. Some of these may be NULL */ + if (element_map->vertex[i]) { + element_map->vertex[i] = &islandbuf[map[element_map->vertex[i] - element_map->storage]]; + } + } + + element_map->island_indices = MEM_callocN(sizeof(*element_map->island_indices) * nislands, + __func__); + element_map->island_total_uvs = MEM_callocN(sizeof(*element_map->island_total_uvs) * nislands, + __func__); + element_map->island_total_unique_uvs = MEM_callocN( + sizeof(*element_map->island_total_unique_uvs) * nislands, __func__); + int j = 0; + for (int i = 0; i < totuv; i++) { + UvElement *next = element_map->storage[i].next; + islandbuf[map[i]].next = next ? &islandbuf[map[next - element_map->storage]] : NULL; + + if (islandbuf[i].island != j) { + j++; + element_map->island_indices[j] = i; + } + BLI_assert(islandbuf[i].island == j); + element_map->island_total_uvs[j]++; + if (islandbuf[i].separate) { + element_map->island_total_unique_uvs[j]++; + } + } + + MEM_SAFE_FREE(element_map->storage); + element_map->storage = islandbuf; + islandbuf = NULL; + element_map->total_islands = nislands; + MEM_SAFE_FREE(stack); + MEM_SAFE_FREE(map); +} + UvElementMap *BM_uv_element_map_create(BMesh *bm, const Scene *scene, const bool uv_selected, @@ -732,26 +861,18 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, BMVert *ev; BMFace *efa; - BMLoop *l; BMIter iter, liter; - /* vars from original func */ - UvElementMap *element_map; - UvElement *buf; - bool *winding = NULL; BLI_buffer_declare_static(vec2f, tf_uv_buf, BLI_BUFFER_NOP, BM_DEFAULT_NGON_STACK_SIZE); - MLoopUV *luv; - int totverts, totfaces, i, totuv, j; - const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + if (cd_loop_uv_offset < 0) { + return NULL; + } BM_mesh_elem_index_ensure(bm, BM_VERT | BM_FACE); - totfaces = bm->totface; - totverts = bm->totvert; - totuv = 0; - - /* generate UvElement array */ + /* Count total uvs. */ + int totuv = 0; BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { continue; @@ -765,6 +886,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, totuv += efa->len; } else { + BMLoop *l; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { totuv++; @@ -777,17 +899,17 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, return NULL; } - element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap"); - element_map->totalUVs = totuv; - element_map->vert = (UvElement **)MEM_callocN(sizeof(*element_map->vert) * totverts, - "UvElementVerts"); - buf = element_map->buf = (UvElement *)MEM_callocN(sizeof(*element_map->buf) * totuv, - "UvElement"); + UvElementMap *element_map = (UvElementMap *)MEM_callocN(sizeof(*element_map), "UvElementMap"); + element_map->total_uvs = totuv; + element_map->vertex = (UvElement **)MEM_callocN(sizeof(*element_map->vertex) * bm->totvert, + "UvElementVerts"); + element_map->storage = (UvElement *)MEM_callocN(sizeof(*element_map->storage) * totuv, + "UvElement"); - if (use_winding) { - winding = MEM_callocN(sizeof(*winding) * totfaces, "winding"); - } + bool *winding = use_winding ? MEM_callocN(sizeof(*winding) * bm->totface, "winding") : NULL; + UvElement *buf = element_map->storage; + int j; BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, j) { if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { @@ -804,18 +926,20 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, tf_uv = (float(*)[2])BLI_buffer_reinit_data(&tf_uv_buf, vec2f, efa->len); } + int i; + BMLoop *l; BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { continue; } buf->l = l; - buf->separate = 0; buf->island = INVALID_ISLAND; buf->loop_of_poly_index = i; - buf->next = element_map->vert[BM_elem_index_get(l->v)]; - element_map->vert[BM_elem_index_get(l->v)] = buf; + /* Insert to head of linked list associated with BMVert. */ + buf->next = element_map->vertex[BM_elem_index_get(l->v)]; + element_map->vertex[BM_elem_index_get(l->v)] = buf; if (use_winding) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); @@ -829,43 +953,54 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, winding[j] = cross_poly_v2(tf_uv, efa->len) > 0; } } + BLI_buffer_free(&tf_uv_buf); - /* sort individual uvs for each vert */ - BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, i) { - UvElement *newvlist = NULL, *vlist = element_map->vert[i]; - UvElement *iterv, *v, *lastv, *next; - const float *uv, *uv2; - bool uv_vert_sel, uv2_vert_sel; - + /* For each BMVert, sort associated linked list into unique uvs. */ + int ev_index; + BM_ITER_MESH_INDEX (ev, &iter, bm, BM_VERTS_OF_MESH, ev_index) { + UvElement *newvlist = NULL; + UvElement *vlist = element_map->vertex[ev_index]; while (vlist) { - v = vlist; + + /* Detach head from unsorted list. */ + UvElement *v = vlist; vlist = vlist->next; v->next = newvlist; newvlist = v; - l = v->l; - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - uv = luv->uv; - uv_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + luv = BM_ELEM_CD_GET_VOID_P(v->l, cd_loop_uv_offset); + const float *uv = luv->uv; + bool uv_vert_sel = uvedit_uv_select_test(scene, v->l, cd_loop_uv_offset); - lastv = NULL; - iterv = vlist; + UvElement *lastv = NULL; + UvElement *iterv = vlist; + /* Scan through unsorted list, finding UvElements which are connected to `v`. */ while (iterv) { - next = iterv->next; + UvElement *next = iterv->next; + luv = BM_ELEM_CD_GET_VOID_P(iterv->l, cd_loop_uv_offset); - l = iterv->l; - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - uv2 = luv->uv; - uv2_vert_sel = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + bool connected = true; /* Assume connected unless we can prove otherwise. */ + + if (connected) { + /* Are the two UVs close together? */ + const float *uv2 = luv->uv; + connected = compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT); + } + + if (connected) { + /* Check if the uv loops share the same selection state (if not, they are not connected + * as they have been ripped or other edit commands have separated them). */ + const bool uv2_vert_sel = uvedit_uv_select_test(scene, iterv->l, cd_loop_uv_offset); + connected = (uv_vert_sel == uv2_vert_sel); + } - /* Check if the uv loops share the same selection state (if not, they are not connected as - * they have been ripped or other edit commands have separated them). */ - const bool connected = (uv_vert_sel == uv2_vert_sel) && - compare_v2v2(uv2, uv, STD_UV_CONNECT_LIMIT); + if (connected && use_winding) { + connected = winding[BM_elem_index_get(iterv->l->f)] == + winding[BM_elem_index_get(v->l->f)]; + } - if (connected && (!use_winding || winding[BM_elem_index_get(iterv->l->f)] == - winding[BM_elem_index_get(v->l->f)])) { + if (connected) { if (lastv) { lastv->next = next; } @@ -882,130 +1017,30 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm, iterv = next; } - newvlist->separate = 1; + element_map->total_unique_uvs++; + newvlist->separate = true; } - element_map->vert[i] = newvlist; + /* Write back sorted list. */ + element_map->vertex[ev_index] = newvlist; } - if (use_winding) { - MEM_freeN(winding); - } + MEM_SAFE_FREE(winding); + /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. + * Now we should sort uv's in islands. */ if (do_islands) { - uint *map; - BMFace **stack; - int stacksize = 0; - UvElement *islandbuf; - /* island number for faces */ - int *island_number = NULL; - - int nislands = 0, islandbufsize = 0; - - /* map holds the map from current vmap->buf to the new, sorted map */ - map = MEM_mallocN(sizeof(*map) * totuv, "uvelement_remap"); - stack = MEM_mallocN(sizeof(*stack) * bm->totface, "uv_island_face_stack"); - islandbuf = MEM_callocN(sizeof(*islandbuf) * totuv, "uvelement_island_buffer"); - island_number = MEM_mallocN(sizeof(*island_number) * totfaces, "uv_island_number_face"); - copy_vn_i(island_number, totfaces, INVALID_ISLAND); - - const bool use_uv_edge_connectivity = scene->toolsettings->uv_flag & UV_SYNC_SELECTION ? - scene->toolsettings->selectmode & SCE_SELECT_EDGE : - scene->toolsettings->uv_selectmode & UV_SELECT_EDGE; - if (use_uv_edge_connectivity) { - nislands = bm_uv_edge_select_build_islands( - element_map, scene, islandbuf, map, uv_selected, cd_loop_uv_offset); - islandbufsize = totuv; - } - - /* at this point, every UvElement in vert points to a UvElement sharing the same vertex. - * Now we should sort uv's in islands. */ - for (i = 0; i < totuv; i++) { - if (element_map->buf[i].island == INVALID_ISLAND) { - element_map->buf[i].island = nislands; - stack[0] = element_map->buf[i].l->f; - island_number[BM_elem_index_get(stack[0])] = nislands; - stacksize = 1; - - while (stacksize > 0) { - efa = stack[--stacksize]; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - if (uv_selected && !uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { - continue; - } - - UvElement *element, *initelement = element_map->vert[BM_elem_index_get(l->v)]; - - for (element = initelement; element; element = element->next) { - if (element->separate) { - initelement = element; - } - - if (element->l->f == efa) { - /* found the uv corresponding to our face and vertex. - * Now fill it to the buffer */ - bm_uv_assign_island( - element_map, element, nislands, map, islandbuf, islandbufsize++); - - for (element = initelement; element; element = element->next) { - if (element->separate && element != initelement) { - break; - } - - if (island_number[BM_elem_index_get(element->l->f)] == INVALID_ISLAND) { - stack[stacksize++] = element->l->f; - island_number[BM_elem_index_get(element->l->f)] = nislands; - } - } - break; - } - } - } - } - - nislands++; - } - } - - MEM_freeN(island_number); - - /* remap */ - for (i = 0; i < bm->totvert; i++) { - /* important since we may do selection only. Some of these may be NULL */ - if (element_map->vert[i]) { - element_map->vert[i] = &islandbuf[map[element_map->vert[i] - element_map->buf]]; - } - } - - element_map->islandIndices = MEM_callocN(sizeof(*element_map->islandIndices) * nislands, - "UvElementMap_island_indices"); - j = 0; - for (i = 0; i < totuv; i++) { - UvElement *element = element_map->buf[i].next; - if (element == NULL) { - islandbuf[map[i]].next = NULL; - } - else { - islandbuf[map[i]].next = &islandbuf[map[element - element_map->buf]]; - } + bm_uv_build_islands(element_map, bm, scene, uv_selected); + } - if (islandbuf[i].island != j) { - j++; - element_map->islandIndices[j] = i; - } + /* TODO: Confirm element_map->total_unique_uvs doesn't require recalculating. */ + element_map->total_unique_uvs = 0; + for (int i = 0; i < element_map->total_uvs; i++) { + if (element_map->storage[i].separate) { + element_map->total_unique_uvs++; } - - MEM_freeN(element_map->buf); - - element_map->buf = islandbuf; - element_map->totalIslands = nislands; - MEM_freeN(stack); - MEM_freeN(map); } - BLI_buffer_free(&tf_uv_buf); - return element_map; } @@ -1025,30 +1060,38 @@ void BM_uv_vert_map_free(UvVertMap *vmap) void BM_uv_element_map_free(UvElementMap *element_map) { if (element_map) { - if (element_map->vert) { - MEM_freeN(element_map->vert); - } - if (element_map->buf) { - MEM_freeN(element_map->buf); - } - if (element_map->islandIndices) { - MEM_freeN(element_map->islandIndices); - } - MEM_freeN(element_map); + MEM_SAFE_FREE(element_map->storage); + MEM_SAFE_FREE(element_map->vertex); + MEM_SAFE_FREE(element_map->head_table); + MEM_SAFE_FREE(element_map->island_indices); + MEM_SAFE_FREE(element_map->island_total_uvs); + MEM_SAFE_FREE(element_map->island_total_unique_uvs); + MEM_SAFE_FREE(element_map); } } -UvElement *BM_uv_element_get(UvElementMap *map, BMFace *efa, BMLoop *l) +UvElement *BM_uv_element_get(UvElementMap *element_map, BMFace *efa, BMLoop *l) { - for (UvElement *element = map->vert[BM_elem_index_get(l->v)]; element; element = element->next) { + UvElement *element = element_map->vertex[BM_elem_index_get(l->v)]; + while (element) { if (element->l->f == efa) { return element; } + element = element->next; } return NULL; } +UvElement *BM_uv_element_get_head(UvElementMap *element_map, UvElement *child) +{ + if (!child) { + return NULL; + } + + return element_map->vertex[BM_elem_index_get(child->l->v)]; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -1518,7 +1561,7 @@ void EDBM_update(Mesh *mesh, const struct EDBMUpdate_Params *params) } if (params->is_destructive) { - /* TODO(campbell): we may be able to remove this now! */ + /* TODO(@campbellbarton): we may be able to remove this now! */ // BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP); } else { diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index 67834bf05ce..971fab1508e 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -444,44 +444,6 @@ bool ED_mesh_color_ensure(Mesh *me, const char *name) return (layer != nullptr); } -bool ED_mesh_color_remove_index(Mesh *me, const int n) -{ - CustomData *ldata = GET_CD_DATA(me, ldata); - CustomDataLayer *cdl; - int index; - - index = CustomData_get_layer_index_n(ldata, CD_PROP_BYTE_COLOR, n); - cdl = (index == -1) ? nullptr : &ldata->layers[index]; - - if (!cdl) { - return false; - } - - delete_customdata_layer(me, cdl); - DEG_id_tag_update(&me->id, 0); - WM_main_add_notifier(NC_GEOM | ND_DATA, me); - - return true; -} -bool ED_mesh_color_remove_active(Mesh *me) -{ - CustomData *ldata = GET_CD_DATA(me, ldata); - const int n = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); - if (n != -1) { - return ED_mesh_color_remove_index(me, n); - } - return false; -} -bool ED_mesh_color_remove_named(Mesh *me, const char *name) -{ - CustomData *ldata = GET_CD_DATA(me, ldata); - const int n = CustomData_get_named_layer(ldata, CD_PROP_BYTE_COLOR, name); - if (n != -1) { - return ED_mesh_color_remove_index(me, n); - } - return false; -} - /*********************** General poll ************************/ static bool layers_poll(bContext *C) @@ -494,25 +456,7 @@ static bool layers_poll(bContext *C) /*********************** Sculpt Vertex colors operators ************************/ -static bool sculpt_vertex_color_remove_poll(bContext *C) -{ - if (!layers_poll(C)) { - return false; - } - - Object *ob = ED_object_context(C); - Mesh *me = static_cast<Mesh *>(ob->data); - CustomData *vdata = GET_CD_DATA(me, vdata); - const int active = CustomData_get_active_layer(vdata, CD_PROP_COLOR); - if (active != -1) { - return true; - } - - return false; -} - -int ED_mesh_sculpt_color_add( - Mesh *me, const char *name, const bool active_set, const bool do_init, ReportList *reports) +int ED_mesh_sculpt_color_add(Mesh *me, const char *name, const bool do_init, ReportList *reports) { /* NOTE: keep in sync with #ED_mesh_uv_add. */ @@ -536,7 +480,7 @@ int ED_mesh_sculpt_color_add( const int layernum_dst = CustomData_get_active_layer(&em->bm->vdata, CD_PROP_COLOR); BM_data_layer_copy(em->bm, &em->bm->vdata, CD_PROP_COLOR, layernum_dst, layernum); } - if (active_set || layernum == 0) { + if (layernum == 0) { CustomData_set_layer_active(&em->bm->vdata, CD_PROP_COLOR, layernum); } } @@ -559,7 +503,7 @@ int ED_mesh_sculpt_color_add( &me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name); } - if (active_set || layernum == 0) { + if (layernum == 0) { CustomData_set_layer_active(&me->vdata, CD_PROP_COLOR, layernum); } @@ -572,58 +516,6 @@ int ED_mesh_sculpt_color_add( return layernum; } -bool ED_mesh_sculpt_color_ensure(Mesh *me, const char *name) -{ - BLI_assert(me->edit_mesh == nullptr); - - if (me->totvert && !CustomData_has_layer(&me->vdata, CD_PROP_COLOR)) { - CustomData_add_layer_named(&me->vdata, CD_PROP_COLOR, CD_DEFAULT, nullptr, me->totvert, name); - BKE_mesh_update_customdata_pointers(me, true); - } - - DEG_id_tag_update(&me->id, 0); - - return (me->mloopcol != nullptr); -} - -bool ED_mesh_sculpt_color_remove_index(Mesh *me, const int n) -{ - CustomData *vdata = GET_CD_DATA(me, vdata); - CustomDataLayer *cdl; - int index; - - index = CustomData_get_layer_index_n(vdata, CD_PROP_COLOR, n); - cdl = (index == -1) ? nullptr : &vdata->layers[index]; - - if (!cdl) { - return false; - } - - delete_customdata_layer(me, cdl); - DEG_id_tag_update(&me->id, 0); - WM_main_add_notifier(NC_GEOM | ND_DATA, me); - - return true; -} -bool ED_mesh_sculpt_color_remove_active(Mesh *me) -{ - CustomData *vdata = GET_CD_DATA(me, vdata); - const int n = CustomData_get_active_layer(vdata, CD_PROP_COLOR); - if (n != -1) { - return ED_mesh_sculpt_color_remove_index(me, n); - } - return false; -} -bool ED_mesh_sculpt_color_remove_named(Mesh *me, const char *name) -{ - CustomData *vdata = GET_CD_DATA(me, vdata); - const int n = CustomData_get_named_layer(vdata, CD_PROP_COLOR, name); - if (n != -1) { - return ED_mesh_sculpt_color_remove_index(me, n); - } - return false; -} - /*********************** UV texture operators ************************/ static bool uv_texture_remove_poll(bContext *C) @@ -709,135 +601,6 @@ void MESH_OT_uv_texture_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/*********************** vertex color operators ************************/ - -static bool vertex_color_remove_poll(bContext *C) -{ - if (!layers_poll(C)) { - return false; - } - - Object *ob = ED_object_context(C); - Mesh *me = static_cast<Mesh *>(ob->data); - CustomData *ldata = GET_CD_DATA(me, ldata); - const int active = CustomData_get_active_layer(ldata, CD_PROP_BYTE_COLOR); - if (active != -1) { - return true; - } - - return false; -} - -static int mesh_vertex_color_add_exec(bContext *C, wmOperator *op) -{ - Object *ob = ED_object_context(C); - Mesh *me = static_cast<Mesh *>(ob->data); - - if (ED_mesh_color_add(me, nullptr, true, true, op->reports) == -1) { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; -} - -void MESH_OT_vertex_color_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Vertex Color"; - ot->description = "Add vertex color layer"; - ot->idname = "MESH_OT_vertex_color_add"; - - /* api callbacks */ - ot->poll = layers_poll; - ot->exec = mesh_vertex_color_add_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int mesh_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_object_context(C); - Mesh *me = static_cast<Mesh *>(ob->data); - - if (!ED_mesh_color_remove_active(me)) { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; -} - -void MESH_OT_vertex_color_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Vertex Color"; - ot->description = "Remove vertex color layer"; - ot->idname = "MESH_OT_vertex_color_remove"; - - /* api callbacks */ - ot->exec = mesh_vertex_color_remove_exec; - ot->poll = vertex_color_remove_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -/*********************** Sculpt Vertex Color Operators ************************/ - -static int mesh_sculpt_vertex_color_add_exec(bContext *C, wmOperator *op) -{ - Object *ob = ED_object_context(C); - Mesh *me = static_cast<Mesh *>(ob->data); - - if (ED_mesh_sculpt_color_add(me, nullptr, true, true, op->reports) == -1) { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; -} - -void MESH_OT_sculpt_vertex_color_add(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Add Sculpt Vertex Color"; - ot->description = "Add vertex color layer"; - ot->idname = "MESH_OT_sculpt_vertex_color_add"; - - /* api callbacks */ - ot->poll = layers_poll; - ot->exec = mesh_sculpt_vertex_color_add_exec; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int mesh_sculpt_vertex_color_remove_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = ED_object_context(C); - Mesh *me = static_cast<Mesh *>(ob->data); - - if (!ED_mesh_sculpt_color_remove_active(me)) { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; -} - -void MESH_OT_sculpt_vertex_color_remove(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Remove Sculpt Vertex Color"; - ot->description = "Remove vertex color layer"; - ot->idname = "MESH_OT_sculpt_vertex_color_remove"; - - /* api callbacks */ - ot->exec = mesh_sculpt_vertex_color_remove_exec; - ot->poll = sculpt_vertex_color_remove_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - /* *** CustomData clear functions, we need an operator for each *** */ static int mesh_customdata_clear_exec__internal(bContext *C, char htype, int type) diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 303234df48c..7c8dbffeb31 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -308,10 +308,6 @@ void MESH_OT_mark_freestyle_face(struct wmOperatorType *ot); void MESH_OT_uv_texture_add(struct wmOperatorType *ot); void MESH_OT_uv_texture_remove(struct wmOperatorType *ot); -void MESH_OT_vertex_color_add(struct wmOperatorType *ot); -void MESH_OT_vertex_color_remove(struct wmOperatorType *ot); -void MESH_OT_sculpt_vertex_color_add(struct wmOperatorType *ot); -void MESH_OT_sculpt_vertex_color_remove(struct wmOperatorType *ot); void MESH_OT_customdata_mask_clear(struct wmOperatorType *ot); void MESH_OT_customdata_skin_add(struct wmOperatorType *ot); void MESH_OT_customdata_skin_clear(struct wmOperatorType *ot); diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index be7f60b0da0..b9e78740e3c 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -134,10 +134,6 @@ void ED_operatortypes_mesh(void) WM_operatortype_append(MESH_OT_uv_texture_add); WM_operatortype_append(MESH_OT_uv_texture_remove); - WM_operatortype_append(MESH_OT_vertex_color_add); - WM_operatortype_append(MESH_OT_vertex_color_remove); - WM_operatortype_append(MESH_OT_sculpt_vertex_color_add); - WM_operatortype_append(MESH_OT_sculpt_vertex_color_remove); WM_operatortype_append(MESH_OT_customdata_mask_clear); WM_operatortype_append(MESH_OT_customdata_skin_add); WM_operatortype_append(MESH_OT_customdata_skin_clear); diff --git a/source/blender/editors/mesh/meshtools.cc b/source/blender/editors/mesh/meshtools.cc index 9e28e1bafdd..b1004b23a21 100644 --- a/source/blender/editors/mesh/meshtools.cc +++ b/source/blender/editors/mesh/meshtools.cc @@ -1329,6 +1329,7 @@ bool ED_mesh_pick_face_vert( */ struct VertPickData { const MVert *mvert; + const bool *hide_vert; const float *mval_f; /* [2] */ ARegion *region; @@ -1343,16 +1344,16 @@ static void ed_mesh_pick_vert__mapFunc(void *userData, const float UNUSED(no[3])) { VertPickData *data = static_cast<VertPickData *>(userData); - if ((data->mvert[index].flag & ME_HIDE) == 0) { - float sco[2]; - - if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) == - V3D_PROJ_RET_OK) { - const float len = len_manhattan_v2v2(data->mval_f, sco); - if (len < data->len_best) { - data->len_best = len; - data->v_idx_best = index; - } + if (data->hide_vert && data->hide_vert[index]) { + return; + } + float sco[2]; + if (ED_view3d_project_float_object(data->region, co, sco, V3D_PROJ_TEST_CLIP_DEFAULT) == + V3D_PROJ_RET_OK) { + const float len = len_manhattan_v2v2(data->mval_f, sco); + if (len < data->len_best) { + data->len_best = len; + data->v_idx_best = index; } } } @@ -1416,6 +1417,8 @@ bool ED_mesh_pick_vert( data.mval_f = mval_f; data.len_best = FLT_MAX; data.v_idx_best = -1; + data.hide_vert = (const bool *)CustomData_get_layer_named( + &me_eval->vdata, CD_PROP_BOOL, ".hide_vert"); BKE_mesh_foreach_mapped_vert(me_eval, ed_mesh_pick_vert__mapFunc, &data, MESH_FOREACH_NOP); diff --git a/source/blender/editors/metaball/mball_edit.c b/source/blender/editors/metaball/mball_edit.c index 06a649e5b6c..6a5d620b546 100644 --- a/source/blender/editors/metaball/mball_edit.c +++ b/source/blender/editors/metaball/mball_edit.c @@ -744,7 +744,7 @@ Base *ED_mball_base_and_elem_from_select_buffer(Base **bases, const uint hit_object = select_id & 0xFFFF; Base *base = NULL; MetaElem *ml = NULL; - /* TODO(campbell): optimize, eg: sort & binary search. */ + /* TODO(@campbellbarton): optimize, eg: sort & binary search. */ for (uint base_index = 0; base_index < bases_len; base_index++) { if (bases[base_index]->object->runtime.select_id == hit_object) { base = bases[base_index]; diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index a2f993c92b9..d05738ca27c 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -53,7 +53,7 @@ set(SRC object_shapekey.c object_transform.cc object_utils.c - object_vgroup.c + object_vgroup.cc object_volume.c object_warp.c diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index b5862d4d957..229f8aace5a 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -259,7 +259,7 @@ void CONSTRAINT_OT_objectsolver_set_inverse(struct wmOperatorType *ot); void CONSTRAINT_OT_objectsolver_clear_inverse(struct wmOperatorType *ot); void CONSTRAINT_OT_followpath_path_animate(struct wmOperatorType *ot); -/* object_vgroup.c */ +/* object_vgroup.cc */ void OBJECT_OT_vertex_group_add(struct wmOperatorType *ot); void OBJECT_OT_vertex_group_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index e7cfcf48fd3..010a01f9d30 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -486,6 +486,9 @@ bool ED_object_modifier_move_to_index(ReportList *reports, } } + /* NOTE: Dependency graph only uses modifier nodes for visibility updates, and exact order of + * modifier nodes in the graph does not matter. */ + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, ob); @@ -1668,6 +1671,7 @@ static int modifier_copy_exec(bContext *C, wmOperator *op) } DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + DEG_relations_tag_update(bmain); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); return OPERATOR_FINISHED; diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index c3d8fb9cfe5..6f7fc2efa61 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -1128,7 +1128,7 @@ static int object_select_all_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } if (any_visible == false) { - /* TODO(campbell): Looks like we could remove this, + /* TODO(@campbellbarton): Looks like we could remove this, * if not comment should say why its needed. */ return OPERATOR_PASS_THROUGH; } diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.cc index 17b7fe7fe5e..f4f5b31e86a 100644 --- a/source/blender/editors/object/object_vgroup.c +++ b/source/blender/editors/object/object_vgroup.cc @@ -5,9 +5,9 @@ * \ingroup edobj */ -#include <math.h> -#include <stddef.h> -#include <string.h> +#include <cmath> +#include <cstddef> +#include <cstring> #include "MEM_guardedalloc.h" @@ -21,14 +21,15 @@ #include "DNA_scene_types.h" #include "DNA_workspace_types.h" -#include "BLI_alloca.h" #include "BLI_array.h" +#include "BLI_array.hh" #include "BLI_bitmap.h" #include "BLI_blenlib.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_utildefines_stack.h" +#include "BLI_vector.hh" #include "BKE_context.h" #include "BKE_customdata.h" @@ -74,7 +75,7 @@ static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob); static bool object_array_for_wpaint_filter(const Object *ob, void *user_data) { - bContext *C = user_data; + bContext *C = static_cast<bContext *>(user_data); if (vertex_group_supported_poll_ex(C, ob)) { return true; } @@ -100,7 +101,7 @@ static bool vertex_group_use_vert_sel(Object *ob) static Lattice *vgroup_edit_lattice(Object *ob) { - Lattice *lt = ob->data; + Lattice *lt = static_cast<Lattice *>(ob->data); BLI_assert(ob->type == OB_LATTICE); return (lt->editlatt) ? lt->editlatt->latt : lt; } @@ -115,7 +116,7 @@ bool ED_vgroup_sync_from_pose(Object *ob) { Object *armobj = BKE_object_pose_armature_get(ob); if (armobj && (armobj->mode & OB_MODE_POSE)) { - struct bArmature *arm = armobj->data; + bArmature *arm = static_cast<bArmature *>(armobj->data); if (arm->act_bone) { int def_num = BKE_object_defgroup_name_index(ob, arm->act_bone->name); if (def_num != -1) { @@ -151,7 +152,7 @@ bool ED_vgroup_parray_alloc(ID *id, const bool use_vert_sel) { *dvert_tot = 0; - *dvert_arr = NULL; + *dvert_arr = nullptr; if (id) { switch (GS(id->name)) { @@ -172,21 +173,23 @@ bool ED_vgroup_parray_alloc(ID *id, i = em->bm->totvert; - *dvert_arr = MEM_mallocN(sizeof(void *) * i, "vgroup parray from me"); + *dvert_arr = static_cast<MDeformVert **>(MEM_mallocN(sizeof(void *) * i, __func__)); *dvert_tot = i; i = 0; if (use_vert_sel) { BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { (*dvert_arr)[i] = BM_elem_flag_test(eve, BM_ELEM_SELECT) ? - BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset) : - NULL; + static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)) : + nullptr; i++; } } else { BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - (*dvert_arr)[i] = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + (*dvert_arr)[i] = static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); i++; } } @@ -198,11 +201,12 @@ bool ED_vgroup_parray_alloc(ID *id, MDeformVert *dvert = me->dvert; *dvert_tot = me->totvert; - *dvert_arr = MEM_mallocN(sizeof(void *) * me->totvert, "vgroup parray from me"); + *dvert_arr = static_cast<MDeformVert **>( + MEM_mallocN(sizeof(void *) * me->totvert, __func__)); if (use_vert_sel) { for (int i = 0; i < me->totvert; i++) { - (*dvert_arr)[i] = (mvert[i].flag & SELECT) ? &dvert[i] : NULL; + (*dvert_arr)[i] = (mvert[i].flag & SELECT) ? &dvert[i] : nullptr; } } else { @@ -222,11 +226,12 @@ bool ED_vgroup_parray_alloc(ID *id, if (lt->dvert) { BPoint *def = lt->def; *dvert_tot = lt->pntsu * lt->pntsv * lt->pntsw; - *dvert_arr = MEM_mallocN(sizeof(void *) * (*dvert_tot), "vgroup parray from me"); + *dvert_arr = static_cast<MDeformVert **>( + MEM_mallocN(sizeof(void *) * (*dvert_tot), __func__)); if (use_vert_sel) { for (int i = 0; i < *dvert_tot; i++) { - (*dvert_arr)[i] = (def->f1 & SELECT) ? <->dvert[i] : NULL; + (*dvert_arr)[i] = (def->f1 & SELECT) ? <->dvert[i] : nullptr; } } else { @@ -255,11 +260,12 @@ void ED_vgroup_parray_mirror_sync(Object *ob, const int vgroup_tot) { BMEditMesh *em = BKE_editmesh_from_object(ob); - MDeformVert **dvert_array_all = NULL; + MDeformVert **dvert_array_all = nullptr; int dvert_tot_all; /* get an array of all verts, not only selected */ - if (ED_vgroup_parray_alloc(ob->data, &dvert_array_all, &dvert_tot_all, false) == false) { + if (ED_vgroup_parray_alloc( + static_cast<ID *>(ob->data), &dvert_array_all, &dvert_tot_all, false) == false) { BLI_assert(0); return; } @@ -271,10 +277,10 @@ void ED_vgroup_parray_mirror_sync(Object *ob, const int *flip_map = BKE_object_defgroup_flip_map(ob, &flip_map_len, true); for (int i_src = 0; i_src < dvert_tot; i_src++) { - if (dvert_array[i_src] != NULL) { + if (dvert_array[i_src] != nullptr) { /* its selected, check if its mirror exists */ int i_dst = ED_mesh_mirror_get_vert(ob, i_src); - if (i_dst != -1 && dvert_array_all[i_dst] != NULL) { + if (i_dst != -1 && dvert_array_all[i_dst] != nullptr) { /* we found a match! */ const MDeformVert *dv_src = dvert_array[i_src]; MDeformVert *dv_dst = dvert_array_all[i_dst]; @@ -294,11 +300,12 @@ void ED_vgroup_parray_mirror_sync(Object *ob, void ED_vgroup_parray_mirror_assign(Object *ob, MDeformVert **dvert_array, const int dvert_tot) { BMEditMesh *em = BKE_editmesh_from_object(ob); - MDeformVert **dvert_array_all = NULL; + MDeformVert **dvert_array_all = nullptr; int dvert_tot_all; /* get an array of all verts, not only selected */ - if (ED_vgroup_parray_alloc(ob->data, &dvert_array_all, &dvert_tot_all, false) == false) { + if (ED_vgroup_parray_alloc( + static_cast<ID *>(ob->data), &dvert_array_all, &dvert_tot_all, false) == false) { BLI_assert(0); return; } @@ -308,7 +315,7 @@ void ED_vgroup_parray_mirror_assign(Object *ob, MDeformVert **dvert_array, const } for (int i = 0; i < dvert_tot; i++) { - if (dvert_array[i] == NULL) { + if (dvert_array[i] == nullptr) { /* its unselected, check if its mirror is */ int i_sel = ED_mesh_mirror_get_vert(ob, i); if ((i_sel != -1) && (i_sel != i) && (dvert_array[i_sel])) { @@ -357,8 +364,8 @@ void ED_vgroup_parray_remove_zero(MDeformVert **dvert_array, bool ED_vgroup_array_copy(Object *ob, Object *ob_from) { - MDeformVert **dvert_array_from = NULL, **dvf; - MDeformVert **dvert_array = NULL, **dv; + MDeformVert **dvert_array_from = nullptr, **dvf; + MDeformVert **dvert_array = nullptr, **dv; int dvert_tot_from; int dvert_tot; int i; @@ -378,17 +385,18 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) /* In case we copy vgroup between two objects using same data, * we only have to care about object side of things. */ if (ob->data != ob_from->data) { - ED_vgroup_parray_alloc(ob_from->data, &dvert_array_from, &dvert_tot_from, false); - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false); + ED_vgroup_parray_alloc( + static_cast<ID *>(ob_from->data), &dvert_array_from, &dvert_tot_from, false); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, false); - if ((dvert_array == NULL) && (dvert_array_from != NULL) && - BKE_object_defgroup_data_create(ob->data)) { - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false); + if ((dvert_array == nullptr) && (dvert_array_from != nullptr) && + BKE_object_defgroup_data_create(static_cast<ID *>(ob->data))) { + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, false); new_vgroup = true; } - if (dvert_tot == 0 || (dvert_tot != dvert_tot_from) || dvert_array_from == NULL || - dvert_array == NULL) { + if (dvert_tot == 0 || (dvert_tot != dvert_tot_from) || dvert_array_from == nullptr || + dvert_array == nullptr) { if (dvert_array) { MEM_freeN(dvert_array); } @@ -413,7 +421,7 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) if (defbase_tot_from < defbase_tot) { /* correct vgroup indices because the number of vgroups is being reduced. */ - int *remap = MEM_mallocN(sizeof(int) * (defbase_tot + 1), __func__); + blender::Array<int> remap(defbase_tot + 1); for (i = 0; i <= defbase_tot_from; i++) { remap[i] = i; } @@ -421,11 +429,10 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) remap[i] = 0; /* can't use these, so disable */ } - BKE_object_defgroup_remap_update_users(ob, remap); - MEM_freeN(remap); + BKE_object_defgroup_remap_update_users(ob, remap.data()); } - if (dvert_array_from != NULL && dvert_array != NULL) { + if (dvert_array_from != nullptr && dvert_array != nullptr) { dvf = dvert_array_from; dv = dvert_array; @@ -434,7 +441,7 @@ bool ED_vgroup_array_copy(Object *ob, Object *ob_from) *(*dv) = *(*dvf); if ((*dv)->dw) { - (*dv)->dw = MEM_dupallocN((*dv)->dw); + (*dv)->dw = static_cast<MDeformWeight *>(MEM_dupallocN((*dv)->dw)); } } @@ -513,7 +520,7 @@ static void mesh_defvert_mirror_update_internal(Object *ob, static void ED_mesh_defvert_mirror_update_em( Object *ob, BMVert *eve, int def_nr, int vidx, const int cd_dvert_offset) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; BMVert *eve_mirr; bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; @@ -521,8 +528,10 @@ static void ED_mesh_defvert_mirror_update_em( eve_mirr = editbmesh_get_x_mirror_vert(ob, em, eve, eve->co, vidx, use_topology); if (eve_mirr && eve_mirr != eve) { - MDeformVert *dvert_src = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); - MDeformVert *dvert_dst = BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset); + MDeformVert *dvert_src = static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); + MDeformVert *dvert_dst = static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset)); mesh_defvert_mirror_update_internal(ob, dvert_dst, dvert_src, def_nr); } } @@ -530,14 +539,14 @@ static void ED_mesh_defvert_mirror_update_em( static void ED_mesh_defvert_mirror_update_ob(Object *ob, int def_nr, int vidx) { int vidx_mirr; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); bool use_topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0; if (vidx == -1) { return; } - vidx_mirr = mesh_get_x_mirror_vert(ob, NULL, vidx, use_topology); + vidx_mirr = mesh_get_x_mirror_vert(ob, nullptr, vidx, use_topology); if ((vidx_mirr) >= 0 && (vidx_mirr != vidx)) { MDeformVert *dvert_src = &me->dvert[vidx]; @@ -548,7 +557,7 @@ static void ED_mesh_defvert_mirror_update_ob(Object *ob, int def_nr, int vidx) void ED_vgroup_vert_active_mirror(Object *ob, int def_nr) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; MDeformVert *dvert_act; @@ -584,7 +593,7 @@ static void vgroup_remove_weight(Object *ob, const int def_nr) static bool vgroup_normalize_active_vertex(Object *ob, eVGroupSelect subset_type) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; BMVert *eve_act; int v_act; @@ -599,7 +608,7 @@ static bool vgroup_normalize_active_vertex(Object *ob, eVGroupSelect subset_type dvert_act = ED_mesh_active_dvert_get_ob(ob, &v_act); } - if (dvert_act == NULL) { + if (dvert_act == nullptr) { return false; } @@ -623,7 +632,7 @@ static bool vgroup_normalize_active_vertex(Object *ob, eVGroupSelect subset_type static void vgroup_copy_active_to_sel(Object *ob, eVGroupSelect subset_type) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; MDeformVert *dvert_act; int i, vgroup_tot, subset_count; @@ -639,7 +648,8 @@ static void vgroup_copy_active_to_sel(Object *ob, eVGroupSelect subset_type) if (dvert_act) { BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && eve != eve_act) { - MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + MDeformVert *dv = static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); BKE_defvert_copy_subset(dv, dvert_act, vgroup_validmap, vgroup_tot); if (me->symmetry & ME_SYMMETRY_X) { ED_mesh_defvert_mirror_update_em(ob, eve, -1, i, cd_dvert_offset); @@ -688,7 +698,7 @@ static const EnumPropertyItem WT_vertex_group_select_item[] = { "Deform Pose Bones", "All Vertex Groups assigned to Deform Bones"}, {WT_VGROUP_ALL, "ALL", 0, "All Groups", "All Vertex Groups"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext *C, @@ -698,10 +708,10 @@ const EnumPropertyItem *ED_object_vgroup_selection_itemf_helper(const bContext * const uint selection_mask) { Object *ob; - EnumPropertyItem *item = NULL; + EnumPropertyItem *item = nullptr; int totitem = 0; - if (C == NULL) { + if (C == nullptr) { /* needed for docs and i18n tools */ return WT_vertex_group_select_item; } @@ -797,13 +807,13 @@ static void ED_vgroup_nr_vert_add( Object *ob, const int def_nr, const int vertnum, const float weight, const int assignmode) { /* Add the vert to the deform group with the specified number. */ - MDeformVert *dvert = NULL; + MDeformVert *dvert = nullptr; int tot; /* Get the vert. */ - BKE_object_defgroup_array_get(ob->data, &dvert, &tot); + BKE_object_defgroup_array_get(static_cast<ID *>(ob->data), &dvert, &tot); - if (dvert == NULL) { + if (dvert == nullptr) { return; } @@ -865,7 +875,7 @@ void ED_vgroup_vert_add(Object *ob, bDeformGroup *dg, int vertnum, float weight, const ListBase *defbase = BKE_object_defgroup_list(ob); const int def_nr = BLI_findindex(defbase, dg); - MDeformVert *dv = NULL; + MDeformVert *dv = nullptr; int tot; /* get the deform group number, exit if @@ -875,8 +885,8 @@ void ED_vgroup_vert_add(Object *ob, bDeformGroup *dg, int vertnum, float weight, /* if there's no deform verts then create some, */ - if (BKE_object_defgroup_array_get(ob->data, &dv, &tot) && dv == NULL) { - BKE_object_defgroup_data_create(ob->data); + if (BKE_object_defgroup_array_get(static_cast<ID *>(ob->data), &dv, &tot) && dv == nullptr) { + BKE_object_defgroup_data_create(static_cast<ID *>(ob->data)); } /* call another function to do the work @@ -891,37 +901,37 @@ void ED_vgroup_vert_remove(Object *ob, bDeformGroup *dg, int vertnum) * deform group. */ - /* TODO(campbell): This is slow in a loop, better pass def_nr directly, + /* TODO(@campbellbarton): This is slow in a loop, better pass def_nr directly, * but leave for later. */ const ListBase *defbase = BKE_object_defgroup_list(ob); const int def_nr = BLI_findindex(defbase, dg); if (def_nr != -1) { - MDeformVert *dvert = NULL; + MDeformVert *dvert = nullptr; int tot; /* get the deform vertices corresponding to the * vertnum */ - BKE_object_defgroup_array_get(ob->data, &dvert, &tot); + BKE_object_defgroup_array_get(static_cast<ID *>(ob->data), &dvert, &tot); if (dvert) { MDeformVert *dv = &dvert[vertnum]; MDeformWeight *dw; dw = BKE_defvert_find_index(dv, def_nr); - BKE_defvert_remove_group(dv, dw); /* dw can be NULL */ + BKE_defvert_remove_group(dv, dw); /* dw can be nullptr */ } } } static float get_vert_def_nr(Object *ob, const int def_nr, const int vertnum) { - MDeformVert *dv = NULL; + MDeformVert *dv = nullptr; /* get the deform vertices corresponding to the vertnum */ if (ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (me->edit_mesh) { BMEditMesh *em = me->edit_mesh; @@ -932,7 +942,7 @@ static float get_vert_def_nr(Object *ob, const int def_nr, const int vertnum) BMVert *eve; BM_mesh_elem_table_ensure(em->bm, BM_VERT); eve = BM_vert_at_index(em->bm, vertnum); - dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + dv = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); } else { return 0.0f; @@ -1004,7 +1014,7 @@ static void vgroup_select_verts(Object *ob, int select) } if (ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (me->edit_mesh) { BMEditMesh *em = me->edit_mesh; @@ -1016,7 +1026,8 @@ static void vgroup_select_verts(Object *ob, int select) BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) { - MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + MDeformVert *dv = static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); if (BKE_defvert_find_index(dv, def_nr)) { BM_vert_select_set(em->bm, eve, select); } @@ -1034,6 +1045,8 @@ static void vgroup_select_verts(Object *ob, int select) } else { if (me->dvert) { + const bool *hide_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert"); MVert *mv; MDeformVert *dv; int i; @@ -1042,7 +1055,7 @@ static void vgroup_select_verts(Object *ob, int select) dv = me->dvert; for (i = 0; i < me->totvert; i++, mv++, dv++) { - if (!(mv->flag & ME_HIDE)) { + if (hide_vert != nullptr && !hide_vert[i]) { if (BKE_defvert_find_index(dv, def_nr)) { if (select) { mv->flag |= SELECT; @@ -1091,12 +1104,13 @@ static void vgroup_duplicate(Object *ob) bDeformGroup *dg, *cdg; char name[sizeof(dg->name)]; MDeformWeight *dw_org, *dw_cpy; - MDeformVert **dvert_array = NULL; + MDeformVert **dvert_array = nullptr; int i, idg, icdg, dvert_tot = 0; ListBase *defbase = BKE_object_defgroup_list_mutable(ob); - dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); + dg = static_cast<bDeformGroup *>( + BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1)); if (!dg) { return; } @@ -1118,8 +1132,8 @@ static void vgroup_duplicate(Object *ob) BKE_object_defgroup_active_index_set(ob, BLI_listbase_count(defbase)); icdg = BKE_object_defgroup_active_index_get(ob) - 1; - /* TODO(campbell): we might want to allow only copy selected verts here? */ - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false); + /* TODO(@campbellbarton): we might want to allow only copy selected verts here? */ + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, false); if (dvert_array) { for (i = 0; i < dvert_tot; i++) { @@ -1140,7 +1154,7 @@ static void vgroup_duplicate(Object *ob) static bool vgroup_normalize(Object *ob) { MDeformWeight *dw; - MDeformVert *dv, **dvert_array = NULL; + MDeformVert *dv, **dvert_array = nullptr; int dvert_tot = 0; const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; @@ -1151,7 +1165,7 @@ static bool vgroup_normalize(Object *ob) return false; } - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { float weight_max = 0.0f; @@ -1198,17 +1212,13 @@ static bool vgroup_normalize(Object *ob) /* This finds all of the vertices face-connected to vert by an edge and returns a * MEM_allocated array of indices of size count. * count is an int passed by reference so it can be assigned the value of the length here. */ -static int *getSurroundingVerts(Mesh *me, int vert, int *count) +static blender::Vector<int> getSurroundingVerts(Mesh *me, int vert) { MPoly *mp = me->mpoly; int i = me->totpoly; - /* Instead of looping twice on all polys and loops, and use a temp array, let's rather - * use a BLI_array, with a reasonable starting/reserved size (typically, there are not - * many vertices face-linked to another one, even 8 might be too high...). */ - int *verts = NULL; - BLI_array_declare(verts); - BLI_array_reserve(verts, 8); + blender::Vector<int> verts; + while (i--) { int j = mp->totloop; int first_l = mp->totloop - 1; @@ -1234,7 +1244,7 @@ static int *getSurroundingVerts(Mesh *me, int vert, int *count) } /* Append a and b verts to array, if not yet present. */ - k = BLI_array_len(verts); + k = verts.size(); /* XXX Maybe a == b is enough? */ while (k-- && !(a == b && a == -1)) { if (verts[k] == a) { @@ -1245,10 +1255,10 @@ static int *getSurroundingVerts(Mesh *me, int vert, int *count) } } if (a != -1) { - BLI_array_append(verts, a); + verts.append(a); } if (b != -1) { - BLI_array_append(verts, b); + verts.append(b); } /* Vert found in this poly, we can go to next one! */ @@ -1259,8 +1269,6 @@ static int *getSurroundingVerts(Mesh *me, int vert, int *count) mp++; } - /* Do not free the array! */ - *count = BLI_array_len(verts); return verts; } @@ -1346,13 +1354,14 @@ static void moveCloserToDistanceFromPlane(Depsgraph *depsgraph, float oldPos[3] = {0}; float vc, hc, dist = 0.0f; int i, k; - float(*changes)[2] = MEM_mallocN(sizeof(float[2]) * totweight, "vertHorzChange"); - float *dists = MEM_mallocN(sizeof(float) * totweight, "distance"); + float(*changes)[2] = static_cast<float(*)[2]>( + MEM_mallocN(sizeof(float[2]) * totweight, "vertHorzChange")); + float *dists = static_cast<float *>(MEM_mallocN(sizeof(float) * totweight, "distance")); /* track if up or down moved it closer for each bone */ - bool *upDown = MEM_callocN(sizeof(bool) * totweight, "upDownTracker"); + bool *upDown = static_cast<bool *>(MEM_callocN(sizeof(bool) * totweight, "upDownTracker")); - int *dwIndices = MEM_callocN(sizeof(int) * totweight, "dwIndexTracker"); + int *dwIndices = static_cast<int *>(MEM_callocN(sizeof(int) * totweight, "dwIndexTracker")); float distToStart; int bestIndex = 0; bool wasChange; @@ -1508,18 +1517,18 @@ static void vgroup_fix( Object *object_eval = DEG_get_evaluated_object(depsgraph, ob); int i; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); MVert *mvert = me->mvert; - int *verts = NULL; if (!(me->editflag & ME_EDIT_PAINT_VERT_SEL)) { return; } for (i = 0; i < me->totvert && mvert; i++, mvert++) { if (mvert->flag & SELECT) { - int count = 0; - if ((verts = getSurroundingVerts(me, i, &count))) { + blender::Vector<int> verts = getSurroundingVerts(me, i); + const int count = verts.size(); + if (!verts.is_empty()) { MVert m; - MVert *p = MEM_callocN(sizeof(MVert) * (count), "deformedPoints"); + MVert *p = static_cast<MVert *>(MEM_callocN(sizeof(MVert) * (count), "deformedPoints")); int k; Mesh *me_deform = mesh_get_eval_deform( @@ -1545,7 +1554,6 @@ static void vgroup_fix( } } - MEM_freeN(verts); MEM_freeN(p); } } @@ -1560,7 +1568,7 @@ static void vgroup_levels_subset(Object *ob, const float gain) { MDeformWeight *dw; - MDeformVert *dv, **dvert_array = NULL; + MDeformVert *dv, **dvert_array = nullptr; int dvert_tot = 0; const bool use_vert_sel = vertex_group_use_vert_sel(ob); @@ -1568,7 +1576,7 @@ static void vgroup_levels_subset(Object *ob, (((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 : false; - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { @@ -1606,7 +1614,7 @@ static bool vgroup_normalize_all(Object *ob, const bool lock_active, ReportList *reports) { - MDeformVert *dv, **dvert_array = NULL; + MDeformVert *dv, **dvert_array = nullptr; int i, dvert_tot = 0; const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; @@ -1617,7 +1625,7 @@ static bool vgroup_normalize_all(Object *ob, return false; } - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { const ListBase *defbase = BKE_object_defgroup_list(ob); @@ -1625,7 +1633,7 @@ static bool vgroup_normalize_all(Object *ob, bool *lock_flags = BKE_object_defgroup_lock_flags_get(ob, defbase_tot); bool changed = false; - if ((lock_active == true) && (lock_flags != NULL) && (def_nr < defbase_tot)) { + if ((lock_active == true) && (lock_flags != nullptr) && (def_nr < defbase_tot)) { lock_flags[def_nr] = true; } @@ -1688,7 +1696,7 @@ static const EnumPropertyItem vgroup_lock_actions[] = { {VGROUP_LOCK, "LOCK", 0, "Lock", "Lock all vertex groups"}, {VGROUP_UNLOCK, "UNLOCK", 0, "Unlock", "Unlock all vertex groups"}, {VGROUP_INVERT, "INVERT", 0, "Invert", "Invert the lock state of all vertex groups"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; enum { @@ -1707,7 +1715,7 @@ static const EnumPropertyItem vgroup_lock_mask[] = { 0, "Invert Unselected", "Apply the opposite of Lock/Unlock to unselected vertex groups"}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; static bool *vgroup_selected_get(Object *ob) @@ -1726,7 +1734,7 @@ static bool *vgroup_selected_get(Object *ob) } } else { - mask = MEM_callocN(defbase_tot * sizeof(bool), __func__); + mask = static_cast<bool *>(MEM_callocN(defbase_tot * sizeof(bool), __func__)); } const int actdef = BKE_object_defgroup_active_index_get(ob); @@ -1740,7 +1748,7 @@ static bool *vgroup_selected_get(Object *ob) static void vgroup_lock_all(Object *ob, int action, int mask) { bDeformGroup *dg; - bool *selected = NULL; + bool *selected = nullptr; int i; if (mask != VGROUP_MASK_ALL) { @@ -1751,7 +1759,7 @@ static void vgroup_lock_all(Object *ob, int action, int mask) if (action == VGROUP_TOGGLE) { action = VGROUP_LOCK; - for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { + for (dg = static_cast<bDeformGroup *>(defbase->first), i = 0; dg; dg = dg->next, i++) { switch (mask) { case VGROUP_MASK_INVERT_UNSELECTED: case VGROUP_MASK_SELECTED: @@ -1774,7 +1782,7 @@ static void vgroup_lock_all(Object *ob, int action, int mask) } } - for (dg = defbase->first, i = 0; dg; dg = dg->next, i++) { + for (dg = static_cast<bDeformGroup *>(defbase->first), i = 0; dg; dg = dg->next, i++) { switch (mask) { case VGROUP_MASK_SELECTED: if (!selected[i]) { @@ -1819,14 +1827,14 @@ static void vgroup_invert_subset(Object *ob, const bool auto_remove) { MDeformWeight *dw; - MDeformVert *dv, **dvert_array = NULL; + MDeformVert *dv, **dvert_array = nullptr; int dvert_tot = 0; const bool use_vert_sel = vertex_group_use_vert_sel(ob); const bool use_mirror = (ob->type == OB_MESH) ? (((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 : false; - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { for (int i = 0; i < dvert_tot; i++) { @@ -1876,10 +1884,10 @@ static void vgroup_smooth_subset(Object *ob, const float fac_expand) { const float ifac = 1.0f - fac; - MDeformVert **dvert_array = NULL; + MDeformVert **dvert_array = nullptr; int dvert_tot = 0; - int *vgroup_subset_map = BLI_array_alloca(vgroup_subset_map, subset_count); - float *vgroup_subset_weights = BLI_array_alloca(vgroup_subset_weights, subset_count); + blender::Array<int, 32> vgroup_subset_map(subset_count); + blender::Array<float, 32> vgroup_subset_weights(subset_count); const bool use_mirror = (ob->type == OB_MESH) ? (((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 : false; @@ -1891,8 +1899,8 @@ static void vgroup_smooth_subset(Object *ob, const float iexpand = 1.0f - expand; BMEditMesh *em = BKE_editmesh_from_object(ob); - BMesh *bm = em ? em->bm : NULL; - Mesh *me = em ? NULL : ob->data; + BMesh *bm = em ? em->bm : nullptr; + Mesh *me = em ? nullptr : static_cast<Mesh *>(ob->data); MeshElemMap *emap; int *emap_mem; @@ -1906,31 +1914,37 @@ static void vgroup_smooth_subset(Object *ob, uint *verts_used; STACK_DECLARE(verts_used); - BKE_object_defgroup_subset_to_index_array(vgroup_validmap, vgroup_tot, vgroup_subset_map); - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, false); - memset(vgroup_subset_weights, 0, sizeof(*vgroup_subset_weights) * subset_count); + BKE_object_defgroup_subset_to_index_array(vgroup_validmap, vgroup_tot, vgroup_subset_map.data()); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, false); + vgroup_subset_weights.fill(0.0f); if (bm) { BM_mesh_elem_table_ensure(bm, BM_VERT); BM_mesh_elem_index_ensure(bm, BM_VERT); - emap = NULL; - emap_mem = NULL; + emap = nullptr; + emap_mem = nullptr; } else { BKE_mesh_vert_edge_map_create(&emap, &emap_mem, me->medge, me->totvert, me->totedge); } - weight_accum_prev = MEM_mallocN(sizeof(*weight_accum_prev) * dvert_tot, __func__); - weight_accum_curr = MEM_mallocN(sizeof(*weight_accum_curr) * dvert_tot, __func__); + weight_accum_prev = static_cast<float *>( + MEM_mallocN(sizeof(*weight_accum_prev) * dvert_tot, __func__)); + weight_accum_curr = static_cast<float *>( + MEM_mallocN(sizeof(*weight_accum_curr) * dvert_tot, __func__)); - verts_used = MEM_mallocN(sizeof(*verts_used) * dvert_tot, __func__); + verts_used = static_cast<uint *>(MEM_mallocN(sizeof(*verts_used) * dvert_tot, __func__)); STACK_INIT(verts_used, dvert_tot); #define IS_BM_VERT_READ(v) (use_hide ? (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == 0) : true) #define IS_BM_VERT_WRITE(v) (use_select ? (BM_elem_flag_test(v, BM_ELEM_SELECT) != 0) : true) -#define IS_ME_VERT_READ(v) (use_hide ? (((v)->flag & ME_HIDE) == 0) : true) + const bool *hide_vert = me ? (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert") : + nullptr; + +#define IS_ME_VERT_READ(v) (use_hide ? (hide_vert && hide_vert[v]) : true) #define IS_ME_VERT_WRITE(v) (use_select ? (((v)->flag & SELECT) != 0) : true) /* initialize used verts */ @@ -1956,8 +1970,8 @@ static void vgroup_smooth_subset(Object *ob, if (IS_ME_VERT_WRITE(v)) { for (int j = 0; j < emap[i].count; j++) { const MEdge *e = &me->medge[emap[i].indices[j]]; - const MVert *v_other = &me->mvert[(e->v1 == i) ? e->v2 : e->v1]; - if (IS_ME_VERT_READ(v_other)) { + const int i_other = (e->v1 == i) ? e->v2 : e->v1; + if (IS_ME_VERT_READ(i_other)) { STACK_PUSH(verts_used, i); break; } @@ -2031,9 +2045,7 @@ static void vgroup_smooth_subset(Object *ob, for (j = 0; j < emap[i].count; j++) { MEdge *e = &me->medge[emap[i].indices[j]]; const int i_other = (e->v1 == i ? e->v2 : e->v1); - MVert *v_other = &me->mvert[i_other]; - - if (IS_ME_VERT_READ(v_other)) { + if (IS_ME_VERT_READ(i_other)) { WEIGHT_ACCUMULATE; } } @@ -2078,9 +2090,9 @@ static void vgroup_smooth_subset(Object *ob, MEM_freeN(dvert_array); } - /* not so efficient to get 'dvert_array' again just so unselected verts are NULL'd */ + /* not so efficient to get 'dvert_array' again just so unselected verts are nullptr'd */ if (use_mirror) { - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, true); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, true); ED_vgroup_parray_mirror_sync(ob, dvert_array, dvert_tot, vgroup_validmap, vgroup_tot); if (dvert_array) { MEM_freeN(dvert_array); @@ -2096,7 +2108,8 @@ static int inv_cmp_mdef_vert_weights(const void *a1, const void *a2) * less than, equal to, or greater than zero corresponding to whether its first argument is * considered less than, equal to, or greater than its second argument. * This does the opposite. */ - const struct MDeformWeight *dw1 = a1, *dw2 = a2; + const MDeformWeight *dw1 = static_cast<const MDeformWeight *>(a1); + const MDeformWeight *dw2 = static_cast<const MDeformWeight *>(a2); if (dw1->weight < dw2->weight) { return 1; @@ -2120,12 +2133,12 @@ static int vgroup_limit_total_subset(Object *ob, const int subset_count, const int max_weights) { - MDeformVert *dv, **dvert_array = NULL; + MDeformVert *dv, **dvert_array = nullptr; int i, dvert_tot = 0; const bool use_vert_sel = vertex_group_use_vert_sel(ob); int remove_tot = 0; - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { int num_to_drop = 0; @@ -2147,7 +2160,8 @@ static int vgroup_limit_total_subset(Object *ob, if (num_to_drop > 0) { /* re-pack dw array so that non-bone weights are first, bone-weighted verts at end * sort the tail, then copy only the truncated array back to dv->dw */ - dw_temp = MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, __func__); + dw_temp = static_cast<MDeformWeight *>( + MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, __func__)); bone_count = 0; non_bone_count = 0; for (j = 0; j < dv->totweight; j++) { @@ -2170,7 +2184,8 @@ static int vgroup_limit_total_subset(Object *ob, dv->totweight -= num_to_drop; /* Do we want to clean/normalize here? */ MEM_freeN(dv->dw); - dv->dw = MEM_reallocN(dw_temp, sizeof(MDeformWeight) * dv->totweight); + dv->dw = static_cast<MDeformWeight *>( + MEM_reallocN(dw_temp, sizeof(MDeformWeight) * dv->totweight)); remove_tot += num_to_drop; } else { @@ -2191,14 +2206,14 @@ static void vgroup_clean_subset(Object *ob, const float epsilon, const bool keep_single) { - MDeformVert **dvert_array = NULL; + MDeformVert **dvert_array = nullptr; int dvert_tot = 0; const bool use_vert_sel = vertex_group_use_vert_sel(ob); const bool use_mirror = (ob->type == OB_MESH) ? (((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 : false; - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { if (use_mirror && use_vert_sel) { @@ -2221,13 +2236,13 @@ static void vgroup_quantize_subset(Object *ob, const int UNUSED(subset_count), const int steps) { - MDeformVert **dvert_array = NULL; + MDeformVert **dvert_array = nullptr; int dvert_tot = 0; const bool use_vert_sel = vertex_group_use_vert_sel(ob); const bool use_mirror = (ob->type == OB_MESH) ? (((Mesh *)ob->data)->symmetry & ME_SYMMETRY_X) != 0 : false; - ED_vgroup_parray_alloc(ob->data, &dvert_array, &dvert_tot, use_vert_sel); + ED_vgroup_parray_alloc(static_cast<ID *>(ob->data), &dvert_array, &dvert_tot, use_vert_sel); if (dvert_array) { const float steps_fl = steps; @@ -2350,7 +2365,7 @@ void ED_vgroup_mirror(Object *ob, BMVert *eve, *eve_mirr; MDeformVert *dvert, *dvert_mirr; char sel, sel_mirr; - int *flip_map = NULL, flip_map_len; + int *flip_map = nullptr, flip_map_len; const int def_nr = BKE_object_defgroup_active_index_get(ob) - 1; int totmirr = 0, totfail = 0; @@ -2359,7 +2374,7 @@ void ED_vgroup_mirror(Object *ob, const ListBase *defbase = BKE_object_defgroup_list(ob); if ((mirror_weights == false && flip_vgroups == false) || - (BLI_findlink(defbase, def_nr) == NULL)) { + (BLI_findlink(defbase, def_nr) == nullptr)) { return; } @@ -2367,21 +2382,21 @@ void ED_vgroup_mirror(Object *ob, flip_map = all_vgroups ? BKE_object_defgroup_flip_map(ob, &flip_map_len, false) : BKE_object_defgroup_flip_map_single(ob, &flip_map_len, false, def_nr); - BLI_assert(flip_map != NULL); + BLI_assert(flip_map != nullptr); - if (flip_map == NULL) { + if (flip_map == nullptr) { /* something went wrong!, possibly no groups */ return; } } else { - flip_map = NULL; + flip_map = nullptr; flip_map_len = 0; } /* only the active group */ if (ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; if (em) { @@ -2406,8 +2421,9 @@ void ED_vgroup_mirror(Object *ob, sel_mirr = BM_elem_flag_test(eve_mirr, BM_ELEM_SELECT); if ((sel || sel_mirr) && (eve != eve_mirr)) { - dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); - dvert_mirr = BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset); + dvert = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); + dvert_mirr = static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve_mirr, cd_dvert_offset)); VGROUP_MIRR_OP; totmirr++; @@ -2432,7 +2448,7 @@ void ED_vgroup_mirror(Object *ob, int vidx, vidx_mirr; const bool use_vert_sel = (me->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; - if (me->dvert == NULL) { + if (me->dvert == nullptr) { goto cleanup; } @@ -2444,7 +2460,7 @@ void ED_vgroup_mirror(Object *ob, for (vidx = 0, mv = me->mvert; vidx < me->totvert; vidx++, mv++) { if (!BLI_BITMAP_TEST(vert_tag, vidx)) { - if ((vidx_mirr = mesh_get_x_mirror_vert(ob, NULL, vidx, use_topology)) != -1) { + if ((vidx_mirr = mesh_get_x_mirror_vert(ob, nullptr, vidx, use_topology)) != -1) { if (vidx != vidx_mirr) { mv_mirr = &me->mvert[vidx_mirr]; if (!BLI_BITMAP_TEST(vert_tag, vidx_mirr)) { @@ -2483,7 +2499,7 @@ void ED_vgroup_mirror(Object *ob, int pntsu_half; /* half but found up odd value */ - if (lt->pntsu == 1 || lt->dvert == NULL) { + if (lt->pntsu == 1 || lt->dvert == nullptr) { goto cleanup; } @@ -2543,7 +2559,8 @@ cleanup: static void vgroup_delete_active(Object *ob) { const ListBase *defbase = BKE_object_defgroup_list(ob); - bDeformGroup *dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); + bDeformGroup *dg = static_cast<bDeformGroup *>( + BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1)); if (!dg) { return; } @@ -2562,7 +2579,7 @@ static void vgroup_assign_verts(Object *ob, const float weight) } if (ob->type == OB_MESH) { - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); if (me->edit_mesh) { BMEditMesh *em = me->edit_mesh; @@ -2582,7 +2599,8 @@ static void vgroup_assign_verts(Object *ob, const float weight) if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { MDeformVert *dv; MDeformWeight *dw; - dv = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); /* can be NULL */ + dv = static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); /* can be nullptr */ dw = BKE_defvert_ensure_index(dv, def_nr); if (dw) { dw->weight = weight; @@ -2615,7 +2633,7 @@ static void vgroup_assign_verts(Object *ob, const float weight) BPoint *bp; int a, tot; - if (lt->dvert == NULL) { + if (lt->dvert == nullptr) { BKE_object_defgroup_data_create(<->id); } @@ -2654,8 +2672,8 @@ static bool vertex_group_supported_poll_ex(bContext *C, const Object *ob) } /* Data checks. */ - const ID *data = ob->data; - if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) { + const ID *data = static_cast<const ID *>(ob->data); + if (data == nullptr || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) { CTX_wm_operator_poll_msg_set(C, "Object type \"%s\" does not have editable data"); return false; } @@ -2711,8 +2729,8 @@ static bool vertex_group_mesh_with_dvert_poll(bContext *C) return false; } - Mesh *me = ob->data; - if (me->dvert == NULL) { + Mesh *me = static_cast<Mesh *>(ob->data); + if (me->dvert == nullptr) { CTX_wm_operator_poll_msg_set(C, "The active mesh object has no vertex group data"); return false; } @@ -2802,7 +2820,7 @@ static bool vertex_group_vert_select_unlocked_poll(bContext *C) const int def_nr = BKE_object_defgroup_active_index_get(ob); if (def_nr != 0) { const ListBase *defbase = BKE_object_defgroup_list(ob); - const bDeformGroup *dg = BLI_findlink(defbase, def_nr - 1); + const bDeformGroup *dg = static_cast<const bDeformGroup *>(BLI_findlink(defbase, def_nr - 1)); if (dg) { return !(dg->flag & DG_LOCK_WEIGHT); } @@ -3004,8 +3022,9 @@ static int vertex_group_remove_from_exec(bContext *C, wmOperator *op) } else { const ListBase *defbase = BKE_object_defgroup_list(ob); - bDeformGroup *dg = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); - if ((dg == NULL) || (BKE_object_defgroup_clear(ob, dg, !use_all_verts) == false)) { + bDeformGroup *dg = static_cast<bDeformGroup *>( + BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1)); + if ((dg == nullptr) || (BKE_object_defgroup_clear(ob, dg, !use_all_verts) == false)) { return OPERATOR_CANCELLED; } } @@ -3056,7 +3075,7 @@ static int vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op)) } vgroup_select_verts(ob, 1); - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); return OPERATOR_FINISHED; @@ -3088,7 +3107,7 @@ static int vertex_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) Object *ob = ED_object_context(C); vgroup_select_verts(ob, 0); - DEG_id_tag_update(ob->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob->data), ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); return OPERATOR_FINISHED; @@ -3155,7 +3174,8 @@ static int vertex_group_levels_exec(bContext *C, wmOperator *op) float offset = RNA_float_get(op->ptr, "offset"); float gain = RNA_float_get(op->ptr, "gain"); - eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + eVGroupSelect subset_type = static_cast<eVGroupSelect>( + RNA_enum_get(op->ptr, "group_select_mode")); int subset_count, vgroup_tot; @@ -3242,7 +3262,8 @@ static int vertex_group_normalize_all_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); bool lock_active = RNA_boolean_get(op->ptr, "lock_active"); - eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + eVGroupSelect subset_type = static_cast<eVGroupSelect>( + RNA_enum_get(op->ptr, "group_select_mode")); bool changed; int subset_count, vgroup_tot; const bool *vgroup_validmap = BKE_object_defgroup_subset_from_select_type( @@ -3302,7 +3323,7 @@ static int vertex_group_fix_exec(bContext *C, wmOperator *op) float distToBe = RNA_float_get(op->ptr, "dist"); float strength = RNA_float_get(op->ptr, "strength"); float cp = RNA_float_get(op->ptr, "accuracy"); - ModifierData *md = ob->modifiers.first; + ModifierData *md = static_cast<ModifierData *>(ob->modifiers.first); while (md) { if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { @@ -3391,9 +3412,9 @@ static int vertex_group_lock_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static char *vertex_group_lock_description(struct bContext *UNUSED(C), - struct wmOperatorType *UNUSED(op), - struct PointerRNA *params) +static char *vertex_group_lock_description(bContext *UNUSED(C), + wmOperatorType *UNUSED(op), + PointerRNA *params) { int action = RNA_enum_get(params, "action"); int mask = RNA_enum_get(params, "mask"); @@ -3414,7 +3435,7 @@ static char *vertex_group_lock_description(struct bContext *UNUSED(C), action_str = TIP_("Invert locks of"); break; default: - return NULL; + return nullptr; } switch (mask) { @@ -3443,7 +3464,7 @@ static char *vertex_group_lock_description(struct bContext *UNUSED(C), } break; default: - return NULL; + return nullptr; } return BLI_sprintfN(TIP_("%s %s vertex groups of the active object"), action_str, target_str); @@ -3491,7 +3512,8 @@ static int vertex_group_invert_exec(bContext *C, wmOperator *op) bool auto_assign = RNA_boolean_get(op->ptr, "auto_assign"); bool auto_remove = RNA_boolean_get(op->ptr, "auto_remove"); - eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + eVGroupSelect subset_type = static_cast<eVGroupSelect>( + RNA_enum_get(op->ptr, "group_select_mode")); int subset_count, vgroup_tot; @@ -3544,7 +3566,8 @@ static int vertex_group_smooth_exec(bContext *C, wmOperator *op) { const float fac = RNA_float_get(op->ptr, "factor"); const int repeat = RNA_int_get(op->ptr, "repeat"); - const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + const eVGroupSelect subset_type = static_cast<eVGroupSelect>( + RNA_enum_get(op->ptr, "group_select_mode")); const float fac_expand = RNA_float_get(op->ptr, "expand"); uint objects_len; @@ -3609,7 +3632,8 @@ static int vertex_group_clean_exec(bContext *C, wmOperator *op) { const float limit = RNA_float_get(op->ptr, "limit"); const bool keep_single = RNA_boolean_get(op->ptr, "keep_single"); - const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + const eVGroupSelect subset_type = static_cast<eVGroupSelect>( + RNA_enum_get(op->ptr, "group_select_mode")); uint objects_len; Object **objects = object_array_for_wpaint(C, &objects_len); @@ -3676,7 +3700,8 @@ static int vertex_group_quantize_exec(bContext *C, wmOperator *op) Object *ob = ED_object_context(C); const int steps = RNA_int_get(op->ptr, "steps"); - eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + eVGroupSelect subset_type = static_cast<eVGroupSelect>( + RNA_enum_get(op->ptr, "group_select_mode")); int subset_count, vgroup_tot; @@ -3719,7 +3744,8 @@ void OBJECT_OT_vertex_group_quantize(wmOperatorType *ot) static int vertex_group_limit_total_exec(bContext *C, wmOperator *op) { const int limit = RNA_int_get(op->ptr, "limit"); - const eVGroupSelect subset_type = RNA_enum_get(op->ptr, "group_select_mode"); + const eVGroupSelect subset_type = static_cast<eVGroupSelect>( + RNA_enum_get(op->ptr, "group_select_mode")); int remove_multi_count = 0; uint objects_len; @@ -3914,13 +3940,13 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C, PropertyRNA *UNUSED(prop), bool *r_free) { - if (C == NULL) { + if (C == nullptr) { return DummyRNA_NULL_items; } Object *ob = ED_object_context(C); EnumPropertyItem tmp = {0, "", 0, "", ""}; - EnumPropertyItem *item = NULL; + EnumPropertyItem *item = nullptr; bDeformGroup *def; int a, totitem = 0; @@ -3929,7 +3955,7 @@ static const EnumPropertyItem *vgroup_itemf(bContext *C, } const ListBase *defbase = BKE_object_defgroup_list(ob); - for (a = 0, def = defbase->first; def; def = def->next, a++) { + for (a = 0, def = static_cast<bDeformGroup *>(defbase->first); def; def = def->next, a++) { tmp.value = a; tmp.icon = ICON_GROUP_VERTEX; tmp.identifier = def->name; @@ -3980,11 +4006,13 @@ static char *vgroup_init_remap(Object *ob) { const ListBase *defbase = BKE_object_defgroup_list(ob); int defbase_tot = BLI_listbase_count(defbase); - char *name_array = MEM_mallocN(MAX_VGROUP_NAME * sizeof(char) * defbase_tot, "sort vgroups"); + char *name_array = static_cast<char *>( + MEM_mallocN(MAX_VGROUP_NAME * sizeof(char) * defbase_tot, "sort vgroups")); char *name; name = name_array; - for (const bDeformGroup *def = defbase->first; def; def = def->next) { + for (const bDeformGroup *def = static_cast<const bDeformGroup *>(defbase->first); def; + def = def->next) { BLI_strncpy(name, def->name, MAX_VGROUP_NAME); name += MAX_VGROUP_NAME; } @@ -3994,20 +4022,21 @@ static char *vgroup_init_remap(Object *ob) static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) { - MDeformVert *dvert = NULL; + MDeformVert *dvert = nullptr; const bDeformGroup *def; const ListBase *defbase = BKE_object_defgroup_list(ob); int defbase_tot = BLI_listbase_count(defbase); /* Needs a dummy index at the start. */ - int *sort_map_update = MEM_mallocN(sizeof(int) * (defbase_tot + 1), "sort vgroups"); + int *sort_map_update = static_cast<int *>( + MEM_mallocN(sizeof(int) * (defbase_tot + 1), __func__)); int *sort_map = sort_map_update + 1; const char *name; int i; name = name_array; - for (def = defbase->first, i = 0; def; def = def->next, i++) { + for (def = static_cast<const bDeformGroup *>(defbase->first), i = 0; def; def = def->next, i++) { sort_map[i] = BLI_findstringindex(defbase, name, offsetof(bDeformGroup, name)); name += MAX_VGROUP_NAME; @@ -4024,7 +4053,7 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) BMVert *eve; BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { - dvert = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + dvert = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); if (dvert->totweight) { BKE_defvert_remap(dvert, sort_map, defbase_tot); } @@ -4042,7 +4071,7 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) /* Grease pencil stores vertex groups separately for each stroke, * so remap each stroke's weights separately. */ if (ob->type == OB_GPENCIL) { - bGPdata *gpd = ob->data; + bGPdata *gpd = static_cast<bGPdata *>(ob->data); LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { @@ -4061,7 +4090,7 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) } } else { - BKE_object_defgroup_array_get(ob->data, &dvert, &dvert_tot); + BKE_object_defgroup_array_get(static_cast<ID *>(ob->data), &dvert, &dvert_tot); /* Create as necessary. */ if (dvert) { @@ -4094,8 +4123,8 @@ static int vgroup_do_remap(Object *ob, const char *name_array, wmOperator *op) static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr) { - const bDeformGroup *def_a = def_a_ptr; - const bDeformGroup *def_b = def_b_ptr; + const bDeformGroup *def_a = static_cast<const bDeformGroup *>(def_a_ptr); + const bDeformGroup *def_b = static_cast<const bDeformGroup *>(def_b_ptr); return BLI_strcasecmp_natural(def_a->name, def_b->name); } @@ -4106,22 +4135,22 @@ static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr) */ static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase) { - if (bonebase == NULL) { + if (bonebase == nullptr) { Object *armobj = BKE_modifiers_is_deformed_by_armature(ob); - if (armobj != NULL) { - bArmature *armature = armobj->data; + if (armobj != nullptr) { + bArmature *armature = static_cast<bArmature *>(armobj->data); bonebase = &armature->bonebase; } } ListBase *defbase = BKE_object_defgroup_list_mutable(ob); - if (bonebase != NULL) { + if (bonebase != nullptr) { Bone *bone; - for (bone = bonebase->last; bone; bone = bone->prev) { + for (bone = static_cast<Bone *>(bonebase->last); bone; bone = bone->prev) { bDeformGroup *dg = BKE_object_defgroup_find_name(ob, bone->name); vgroup_sort_bone_hierarchy(ob, &bone->childbase); - if (dg != NULL) { + if (dg != nullptr) { BLI_remlink(defbase, dg); BLI_addhead(defbase, dg); } @@ -4152,7 +4181,7 @@ static int vertex_group_sort_exec(bContext *C, wmOperator *op) BLI_listbase_sort(defbase, vgroup_sort_name); break; case SORT_TYPE_BONEHIERARCHY: - vgroup_sort_bone_hierarchy(ob, NULL); + vgroup_sort_bone_hierarchy(ob, nullptr); break; } @@ -4176,7 +4205,7 @@ void OBJECT_OT_vertex_group_sort(wmOperatorType *ot) static const EnumPropertyItem vgroup_sort_type[] = { {SORT_TYPE_NAME, "NAME", 0, "Name", ""}, {SORT_TYPE_BONEHIERARCHY, "BONE_HIERARCHY", 0, "Bone Hierarchy", ""}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; ot->name = "Sort Vertex Groups"; @@ -4209,7 +4238,8 @@ static int vgroup_move_exec(bContext *C, wmOperator *op) ListBase *defbase = BKE_object_defgroup_list_mutable(ob); - def = BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1); + def = static_cast<bDeformGroup *>( + BLI_findlink(defbase, BKE_object_defgroup_active_index_get(ob) - 1)); if (!def) { return OPERATOR_CANCELLED; } @@ -4237,7 +4267,7 @@ void OBJECT_OT_vertex_group_move(wmOperatorType *ot) static const EnumPropertyItem vgroup_slot_move[] = { {-1, "UP", 0, "Up", ""}, {1, "DOWN", 0, "Down", ""}, - {0, NULL, 0, NULL, NULL}, + {0, nullptr, 0, nullptr, nullptr}, }; /* identifiers */ @@ -4270,7 +4300,7 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) { MDeformVert *dvert_act; - Mesh *me = ob->data; + Mesh *me = static_cast<Mesh *>(ob->data); BMEditMesh *em = me->edit_mesh; int i; @@ -4280,13 +4310,14 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) BMVert *eve, *eve_act; dvert_act = ED_mesh_active_dvert_get_em(ob, &eve_act); - if (dvert_act == NULL) { + if (dvert_act == nullptr) { return; } BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, i) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT) && (eve != eve_act)) { - MDeformVert *dvert_dst = BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset); + MDeformVert *dvert_dst = static_cast<MDeformVert *>( + BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset)); BKE_defvert_copy_index(dvert_dst, def_nr, dvert_act, def_nr); @@ -4305,7 +4336,7 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) int v_act; dvert_act = ED_mesh_active_dvert_get_ob(ob, &v_act); - if (dvert_act == NULL) { + if (dvert_act == nullptr) { return; } @@ -4330,7 +4361,7 @@ static void vgroup_copy_active_to_sel_single(Object *ob, const int def_nr) static bool check_vertex_group_accessible(wmOperator *op, Object *ob, int def_nr) { const ListBase *defbase = BKE_object_defgroup_list(ob); - bDeformGroup *dg = BLI_findlink(defbase, def_nr); + bDeformGroup *dg = static_cast<bDeformGroup *>(BLI_findlink(defbase, def_nr)); if (!dg) { BKE_report(op->reports, RPT_ERROR, "Invalid vertex group index"); @@ -4387,7 +4418,7 @@ void OBJECT_OT_vertex_weight_paste(wmOperatorType *ot) "Index of source weight in active vertex group", -1, INT_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); } /** \} */ @@ -4437,7 +4468,7 @@ void OBJECT_OT_vertex_weight_delete(wmOperatorType *ot) "Index of source weight in active vertex group", -1, INT_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); } /** \} */ @@ -4484,7 +4515,7 @@ void OBJECT_OT_vertex_weight_set_active(wmOperatorType *ot) "Index of source weight in active vertex group", -1, INT_MAX); - RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + RNA_def_property_flag(prop, (PropertyFlag)(PROP_SKIP_SAVE | PROP_HIDDEN)); } /** \} */ @@ -4497,7 +4528,7 @@ static int vertex_weight_normalize_active_vertex_exec(bContext *C, wmOperator *U { Object *ob = ED_object_context(C); ToolSettings *ts = CTX_data_tool_settings(C); - eVGroupSelect subset_type = ts->vgroupsubset; + eVGroupSelect subset_type = static_cast<eVGroupSelect>(ts->vgroupsubset); bool changed; changed = vgroup_normalize_active_vertex(ob, subset_type); @@ -4536,7 +4567,7 @@ static int vertex_weight_copy_exec(bContext *C, wmOperator *UNUSED(op)) { Object *ob = ED_object_context(C); ToolSettings *ts = CTX_data_tool_settings(C); - eVGroupSelect subset_type = ts->vgroupsubset; + eVGroupSelect subset_type = static_cast<eVGroupSelect>(ts->vgroupsubset); vgroup_copy_active_to_sel(ob, subset_type); diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c index e8ceb97ed7a..1ce90849a88 100644 --- a/source/blender/editors/physics/dynamicpaint_ops.c +++ b/source/blender/editors/physics/dynamicpaint_ops.c @@ -21,6 +21,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_dynamicpaint.h" @@ -233,7 +234,7 @@ static int output_toggle_exec(bContext *C, wmOperator *op) ED_mesh_color_add(ob->data, name, true, true, op->reports); } else { - ED_mesh_color_remove_named(ob->data, name); + BKE_id_attribute_remove(ob->data, name, NULL); } } /* Vertex Weight Layer */ diff --git a/source/blender/editors/render/render_opengl.cc b/source/blender/editors/render/render_opengl.cc index 77ad23f1e3f..e91bffce2c2 100644 --- a/source/blender/editors/render/render_opengl.cc +++ b/source/blender/editors/render/render_opengl.cc @@ -278,19 +278,10 @@ static void screen_opengl_views_setup(OGLRender *oglrender) static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, RenderResult *rr) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = oglrender->scene; - ARegion *region = oglrender->region; - View3D *v3d = oglrender->v3d; - RegionView3D *rv3d = oglrender->rv3d; Object *camera = nullptr; int sizex = oglrender->sizex; int sizey = oglrender->sizey; - const short view_context = (v3d != nullptr); - bool draw_sky = (scene->r.alphamode == R_ADDSKY); - float *rectf = nullptr; - uchar *rect = nullptr; - const char *viewname = RE_GetActiveRenderView(oglrender->re); ImBuf *ibuf_result = nullptr; if (oglrender->is_sequencer) { @@ -301,7 +292,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R ImBuf *ibuf = oglrender->seq_data.ibufs_arr[oglrender->view_id]; if (ibuf) { - ImBuf *out = IMB_dupImBuf(ibuf); + ibuf_result = IMB_dupImBuf(ibuf); IMB_freeImBuf(ibuf); /* OpenGL render is considered to be preview and should be * as fast as possible. So currently we're making sure sequencer @@ -310,25 +301,21 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R * TODO(sergey): In the case of output to float container (EXR) * it actually makes sense to keep float buffer instead. */ - if (out->rect_float != nullptr) { - IMB_rect_from_float(out); - imb_freerectfloatImBuf(out); + if (ibuf_result->rect_float != nullptr) { + IMB_rect_from_float(ibuf_result); + imb_freerectfloatImBuf(ibuf_result); } - BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y)); - RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id); - IMB_freeImBuf(out); + BLI_assert((sizex == ibuf->x) && (sizey == ibuf->y)); } else if (gpd) { /* If there are no strips, Grease Pencil still needs a buffer to draw on */ - ImBuf *out = IMB_allocImBuf(oglrender->sizex, oglrender->sizey, 32, IB_rect); - RE_render_result_rect_from_ibuf(rr, out, oglrender->view_id); - IMB_freeImBuf(out); + ibuf_result = IMB_allocImBuf(sizex, sizey, 32, IB_rect); } if (gpd) { int i; uchar *gp_rect; - uchar *render_rect = (uchar *)RE_RenderViewGetById(rr, oglrender->view_id)->rect32; + uchar *render_rect = (uchar *)ibuf_result->rect; DRW_opengl_context_enable(); GPU_offscreen_bind(oglrender->ofs, true); @@ -359,10 +346,16 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R } else { /* shouldn't suddenly give errors mid-render but possible */ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); char err_out[256] = "unknown"; ImBuf *ibuf_view; + bool draw_sky = (scene->r.alphamode == R_ADDSKY); const int alpha_mode = (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL; - if (view_context) { + const char *viewname = RE_GetActiveRenderView(oglrender->re); + View3D *v3d = oglrender->v3d; + + if (v3d != nullptr) { + ARegion *region = oglrender->region; ibuf_view = ED_view3d_draw_offscreen_imbuf(depsgraph, scene, static_cast<eDrawType>(v3d->shading.type), @@ -378,7 +371,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R err_out); /* for stamp only */ - if (rv3d->persp == RV3D_CAMOB && v3d->camera) { + if (oglrender->rv3d->persp == RV3D_CAMOB && v3d->camera) { camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname); } } @@ -388,8 +381,8 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R nullptr, OB_SOLID, scene->camera, - oglrender->sizex, - oglrender->sizey, + sizex, + sizey, IB_rectfloat, V3D_OFSDRAW_SHOW_ANNOTATION, alpha_mode, @@ -401,12 +394,6 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R if (ibuf_view) { ibuf_result = ibuf_view; - if (ibuf_view->rect_float) { - rectf = ibuf_view->rect_float; - } - else { - rect = (uchar *)ibuf_view->rect; - } } else { fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out); @@ -415,6 +402,14 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R if (ibuf_result != nullptr) { if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) { + float *rectf = nullptr; + uchar *rect = nullptr; + if (ibuf_result->rect_float) { + rectf = ibuf_result->rect_float; + } + else { + rect = (uchar *)ibuf_result->rect; + } BKE_image_stamp_buf(scene, camera, nullptr, rect, rectf, rr->rectx, rr->recty, 4); } RE_render_result_rect_from_ibuf(rr, ibuf_result, oglrender->view_id); diff --git a/source/blender/editors/render/render_preview.cc b/source/blender/editors/render/render_preview.cc index 97bbcaa102f..cd0a05f02bc 100644 --- a/source/blender/editors/render/render_preview.cc +++ b/source/blender/editors/render/render_preview.cc @@ -1771,7 +1771,7 @@ PreviewLoadJob &PreviewLoadJob::ensure_job(wmWindowManager *wm, wmWindow *win) WM_jobs_start(wm, wm_job); } - return *reinterpret_cast<PreviewLoadJob *>(WM_jobs_customdata_get(wm_job)); + return *static_cast<PreviewLoadJob *>(WM_jobs_customdata_get(wm_job)); } void PreviewLoadJob::load_jobless(PreviewImage *preview, const eIconSizes icon_size) @@ -1807,11 +1807,11 @@ void PreviewLoadJob::run_fn(void *customdata, short *do_update, float *UNUSED(progress)) { - PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + PreviewLoadJob *job_data = static_cast<PreviewLoadJob *>(customdata); IMB_thumb_locks_acquire(); - while (RequestedPreview *request = reinterpret_cast<RequestedPreview *>( + while (RequestedPreview *request = static_cast<RequestedPreview *>( BLI_thread_queue_pop_timeout(job_data->todo_queue_, 100))) { if (*stop) { break; @@ -1864,7 +1864,7 @@ void PreviewLoadJob::finish_request(RequestedPreview &request) void PreviewLoadJob::update_fn(void *customdata) { - PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + PreviewLoadJob *job_data = static_cast<PreviewLoadJob *>(customdata); for (auto request_it = job_data->requested_previews_.begin(); request_it != job_data->requested_previews_.end();) { @@ -1884,7 +1884,7 @@ void PreviewLoadJob::update_fn(void *customdata) void PreviewLoadJob::end_fn(void *customdata) { - PreviewLoadJob *job_data = reinterpret_cast<PreviewLoadJob *>(customdata); + PreviewLoadJob *job_data = static_cast<PreviewLoadJob *>(customdata); /* Finish any possibly remaining queued previews. */ for (RequestedPreview &request : job_data->requested_previews_) { @@ -1895,7 +1895,7 @@ void PreviewLoadJob::end_fn(void *customdata) void PreviewLoadJob::free_fn(void *customdata) { - MEM_delete(reinterpret_cast<PreviewLoadJob *>(customdata)); + MEM_delete(static_cast<PreviewLoadJob *>(customdata)); } static void icon_preview_free(void *customdata) diff --git a/source/blender/editors/render/render_update.cc b/source/blender/editors/render/render_update.cc index 3d26e764211..7cefcf9815e 100644 --- a/source/blender/editors/render/render_update.cc +++ b/source/blender/editors/render/render_update.cc @@ -95,20 +95,20 @@ void ED_render_view3d_update(Depsgraph *depsgraph, CTX_free(C); } - else { - RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->shading.type); - if (updated) { - DRWUpdateContext drw_context = {nullptr}; - drw_context.bmain = bmain; - drw_context.depsgraph = depsgraph; - drw_context.scene = scene; - drw_context.view_layer = view_layer; - drw_context.region = region; - drw_context.v3d = v3d; - drw_context.engine_type = engine_type; - DRW_notify_view_update(&drw_context); - } + + if (!updated) { + continue; } + + DRWUpdateContext drw_context = {nullptr}; + drw_context.bmain = bmain; + drw_context.depsgraph = depsgraph; + drw_context.scene = scene; + drw_context.view_layer = view_layer; + drw_context.region = region; + drw_context.v3d = v3d; + drw_context.engine_type = ED_view3d_engine_type(scene, v3d->shading.type); + DRW_notify_view_update(&drw_context); } } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 0d6b6ee1d78..83e6c837eac 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -1032,15 +1032,13 @@ static eContextResult screen_ctx_sel_actions_impl(const bContext *C, CTX_data_id_pointer_set(result, (ID *)action); break; } - else { - if (editable && ID_IS_LINKED(action)) { - continue; - } + if (editable && ID_IS_LINKED(action)) { + continue; + } - /* Add the action to the output list if not already added. */ - if (BLI_gset_add(seen_set, action)) { - CTX_data_id_list_add(result, &action->id); - } + /* Add the action to the output list if not already added. */ + if (BLI_gset_add(seen_set, action)) { + CTX_data_id_list_add(result, &action->id); } } } diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 08c8c863729..73195168d38 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -110,7 +110,7 @@ ScrArea *area_split(const wmWindow *win, return NULL; } - /* NOTE(campbell): regarding (fac > 0.5f) checks below. + /* NOTE(@campbellbarton): regarding (fac > 0.5f) checks below. * normally it shouldn't matter which is used since the copy should match the original * however with viewport rendering and python console this isn't the case. */ diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index cb29f15420c..fc3ac53ef0b 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -220,7 +220,7 @@ WorkSpace *ED_workspace_duplicate(WorkSpace *workspace_old, Main *bmain, wmWindo workspace_new->order = workspace_old->order; BLI_duplicatelist(&workspace_new->owner_ids, &workspace_old->owner_ids); - /* TODO(campbell): tools */ + /* TODO(@campbellbarton): tools */ LISTBASE_FOREACH (WorkSpaceLayout *, layout_old, &workspace_old->layouts) { WorkSpaceLayout *layout_new = ED_workspace_layout_duplicate( diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index edb0f1cda4d..b170280ccf3 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -37,8 +37,8 @@ set(SRC curves_sculpt_ops.cc curves_sculpt_pinch.cc curves_sculpt_puff.cc - curves_sculpt_selection_paint.cc curves_sculpt_selection.cc + curves_sculpt_selection_paint.cc curves_sculpt_slide.cc curves_sculpt_smooth.cc curves_sculpt_snake_hook.cc diff --git a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc index 5bfc8ccc667..a955a074df2 100644 --- a/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc +++ b/source/blender/editors/sculpt_paint/curves_sculpt_selection.cc @@ -67,10 +67,11 @@ static IndexMask retrieve_selected_curves(const CurvesGeometry &curves, return selection.get_internal_single() <= 0.0f ? IndexMask(0) : IndexMask(curves.curves_num()); } + const Span<float> point_selection_span = selection.get_internal_span(); return index_mask_ops::find_indices_based_on_predicate( curves.curves_range(), 512, r_indices, [&](const int curve_i) { for (const int i : curves.points_for_curve(curve_i)) { - if (selection[i] > 0.0f) { + if (point_selection_span[i] > 0.0f) { return true; } } diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index c5ebcf870a3..577540725af 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1145,11 +1145,10 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, } GPU_line_width(1.0f); - if (ss->preview_vert_index_count > 0) { - immBegin(GPU_PRIM_LINES, ss->preview_vert_index_count); - for (int i = 0; i < ss->preview_vert_index_count; i++) { - immVertex3fv(gpuattr, - SCULPT_vertex_co_for_grab_active_get(ss, ss->preview_vert_index_list[i])); + if (ss->preview_vert_count > 0) { + immBegin(GPU_PRIM_LINES, ss->preview_vert_count); + for (int i = 0; i < ss->preview_vert_count; i++) { + immVertex3fv(gpuattr, SCULPT_vertex_co_for_grab_active_get(ss, ss->preview_vert_list[i])); } immEnd(); } @@ -1209,7 +1208,7 @@ typedef struct PaintCursorContext { /* Sculpt related data. */ Sculpt *sd; SculptSession *ss; - int prev_active_vertex_index; + PBVHVertRef prev_active_vertex; bool is_stroke_active; bool is_cursor_over_mesh; bool is_multires; @@ -1366,7 +1365,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon /* This updates the active vertex, which is needed for most of the Sculpt/Vertex Colors tools to * work correctly */ - pcontext->prev_active_vertex_index = ss->active_vertex_index; + pcontext->prev_active_vertex = ss->active_vertex; if (!ups->stroke_active) { pcontext->is_cursor_over_mesh = SCULPT_cursor_geometry_info_update( C, &gi, mval_fl, (pcontext->brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE)); @@ -1549,7 +1548,7 @@ static void paint_cursor_preview_boundary_data_update(PaintCursorContext *pconte } ss->boundary_preview = SCULPT_boundary_data_init( - pcontext->vc.obact, pcontext->brush, ss->active_vertex_index, pcontext->radius); + pcontext->vc.obact, pcontext->brush, ss->active_vertex, pcontext->radius); } static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext *pcontext) @@ -1575,8 +1574,8 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * paint_cursor_update_object_space_radius(pcontext); - const bool update_previews = pcontext->prev_active_vertex_index != - SCULPT_active_vertex_get(pcontext->ss); + const bool update_previews = pcontext->prev_active_vertex.i != + SCULPT_active_vertex_get(pcontext->ss).i; /* Setup drawing. */ wmViewport(&pcontext->region->winrct); diff --git a/source/blender/editors/sculpt_paint/paint_hide.c b/source/blender/editors/sculpt_paint/paint_hide.c index 944b3f953a0..c904d533db8 100644 --- a/source/blender/editors/sculpt_paint/paint_hide.c +++ b/source/blender/editors/sculpt_paint/paint_hide.c @@ -78,6 +78,12 @@ static void partialvis_update_mesh(Object *ob, BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); paint_mask = CustomData_get_layer(&me->vdata, CD_PAINT_MASK); + bool *hide_vert = CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"); + if (hide_vert == NULL) { + hide_vert = CustomData_add_layer_named( + &me->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, me->totvert, ".hide_vert"); + } + SCULPT_undo_push_node(ob, node, SCULPT_UNDO_HIDDEN); for (i = 0; i < totvert; i++) { @@ -86,16 +92,11 @@ static void partialvis_update_mesh(Object *ob, /* Hide vertex if in the hide volume. */ if (is_effected(area, planes, v->co, vmask)) { - if (action == PARTIALVIS_HIDE) { - v->flag |= ME_HIDE; - } - else { - v->flag &= ~ME_HIDE; - } + hide_vert[vert_indices[i]] = (action == PARTIALVIS_HIDE); any_changed = true; } - if (!(v->flag & ME_HIDE)) { + if (!hide_vert[vert_indices[i]]) { any_visible = true; } } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 9449cc6eb8d..3e5ad9bdc2d 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1223,12 +1223,12 @@ static VertSeam *find_adjacent_seam(const ProjPaintState *ps, /* Circulate through the (sorted) vert seam array, in the direction of the seam normal, * until we find the first opposing seam, matching in UV space. */ if (seam->normal_cw) { - LISTBASE_CIRCULAR_BACKWARD_BEGIN (vert_seams, adjacent, seam) { + LISTBASE_CIRCULAR_BACKWARD_BEGIN (VertSeam *, vert_seams, adjacent, seam) { if ((adjacent->normal_cw != seam->normal_cw) && cmp_uv(adjacent->uv, seam->uv)) { break; } } - LISTBASE_CIRCULAR_BACKWARD_END(vert_seams, adjacent, seam); + LISTBASE_CIRCULAR_BACKWARD_END(VertSeam *, vert_seams, adjacent, seam); } else { LISTBASE_CIRCULAR_FORWARD_BEGIN (vert_seams, adjacent, seam) { diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index b861d0c84da..2e57886fd95 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -663,7 +663,7 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd) { float vertex_normal[3]; - SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal); + SCULPT_vertex_normal_get(sgcontext->ss, vd->vertex, vertex_normal); float dot = dot_v3v3(sgcontext->view_normal, vertex_normal); const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f); @@ -743,7 +743,7 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata, BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { - SCULPT_vertex_face_set_set(sgcontext->ss, vd.index, face_set_operation->new_face_set_id); + SCULPT_vertex_face_set_set(sgcontext->ss, vd.vertex, face_set_operation->new_face_set_id); any_updated = true; } } @@ -1025,7 +1025,9 @@ static void sculpt_gesture_trim_calculate_depth(SculptGestureContext *sgcontext) trim_operation->depth_back = -FLT_MAX; for (int i = 0; i < totvert; i++) { - const float *vco = SCULPT_vertex_co_get(ss, i); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + const float *vco = SCULPT_vertex_co_get(ss, vertex); /* Convert the coordinates to world space to calculate the depth. When generating the trimming * mesh, coordinates are first calculated in world space, then converted to object space to * store them. */ @@ -1437,7 +1439,7 @@ static void project_line_gesture_apply_task_cb(void *__restrict userdata, } add_v3_v3(vd.co, disp); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(sgcontext->ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(sgcontext->ss->pbvh, vd.vertex); } any_updated = true; } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index aa151b2e678..ffa931268fd 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -122,22 +122,22 @@ int SCULPT_vertex_count_get(SculptSession *ss) return 0; } -const float *SCULPT_vertex_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_co_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { if (ss->shapekey_active || ss->deform_modifiers_active) { const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); - return mverts[index].co; + return mverts[vertex.i].co; } - return ss->mvert[index].co; + return ss->mvert[vertex.i].co; } case PBVH_BMESH: - return BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->co; + return ((BMVert *)vertex.i)->co; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; return CCG_elem_co(key, CCG_elem_offset(key, elem, vertex_index)); } @@ -158,31 +158,33 @@ bool SCULPT_has_colors(const SculptSession *ss) return ss->vcol || ss->mcol; } -void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]) +void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]) { - BKE_pbvh_vertex_color_get(ss->pbvh, index, r_color); + BKE_pbvh_vertex_color_get(ss->pbvh, vertex, r_color); } -void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]) +void SCULPT_vertex_color_set(SculptSession *ss, PBVHVertRef vertex, const float color[4]) { - BKE_pbvh_vertex_color_set(ss->pbvh, index, color); + BKE_pbvh_vertex_color_set(ss->pbvh, vertex, color); } -void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { const float(*vert_normals)[3] = BKE_pbvh_get_vert_normals(ss->pbvh); - copy_v3_v3(no, vert_normals[index]); + copy_v3_v3(no, vert_normals[vertex.i]); break; } - case PBVH_BMESH: - copy_v3_v3(no, BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index)->no); + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + copy_v3_v3(no, v->no); break; + } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; copy_v3_v3(no, CCG_elem_no(key, CCG_elem_offset(key, elem, vertex_index))); break; @@ -190,42 +192,42 @@ void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]) } } -const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index) +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex) { if (ss->persistent_base) { - return ss->persistent_base[index].co; + return ss->persistent_base[BKE_pbvh_vertex_to_index(ss->pbvh, vertex)].co; } - return SCULPT_vertex_co_get(ss, index); + return SCULPT_vertex_co_get(ss, vertex); } -const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index) +const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, PBVHVertRef vertex) { - /* Always grab active shape key if the sculpt happens on shapekey. */ - if (ss->shapekey_active) { - const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); - return mverts[index].co; - } + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + /* Always grab active shape key if the sculpt happens on shapekey. */ + if (ss->shapekey_active) { + const MVert *mverts = BKE_pbvh_get_verts(ss->pbvh); + return mverts[vertex.i].co; + } - /* Sculpting on the base mesh. */ - if (ss->mvert) { - return ss->mvert[index].co; + /* Sculpting on the base mesh. */ + return ss->mvert[vertex.i].co; } /* Everything else, such as sculpting on multires. */ - return SCULPT_vertex_co_get(ss, index); + return SCULPT_vertex_co_get(ss, vertex); } -void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]) +void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, float r_co[3]) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: case PBVH_BMESH: - copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(r_co, SCULPT_vertex_co_get(ss, vertex)); break; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, @@ -236,30 +238,30 @@ void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3] } } -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]) +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]) { if (ss->persistent_base) { - copy_v3_v3(no, ss->persistent_base[index].no); + copy_v3_v3(no, ss->persistent_base[vertex.i].no); return; } - SCULPT_vertex_normal_get(ss, index, no); + SCULPT_vertex_normal_get(ss, vertex, no); } -float SCULPT_vertex_mask_get(SculptSession *ss, int index) +float SCULPT_vertex_mask_get(SculptSession *ss, PBVHVertRef vertex) { BMVert *v; float *mask; switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - return ss->vmask[index]; + return ss->vmask[vertex.i]; case PBVH_BMESH: - v = BM_vert_at_index(BKE_pbvh_get_bmesh(ss->pbvh), index); + v = (BMVert *)vertex.i; mask = BM_ELEM_CD_GET_VOID_P(v, CustomData_get_offset(&ss->bm->vdata, CD_PAINT_MASK)); return *mask; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; CCGElem *elem = BKE_pbvh_get_grids(ss->pbvh)[grid_index]; return *CCG_elem_mask(key, CCG_elem_offset(key, elem, vertex_index)); } @@ -268,12 +270,13 @@ float SCULPT_vertex_mask_get(SculptSession *ss, int index) return 0.0f; } -int SCULPT_active_vertex_get(SculptSession *ss) +PBVHVertRef SCULPT_active_vertex_get(SculptSession *ss) { if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_BMESH, PBVH_GRIDS)) { - return ss->active_vertex_index; + return ss->active_vertex; } - return 0; + + return BKE_pbvh_make_vref(PBVH_REF_NONE); } const float *SCULPT_active_vertex_co_get(SculptSession *ss) @@ -338,32 +341,37 @@ int SCULPT_active_face_set_get(SculptSession *ss) return SCULPT_FACE_SET_NONE; } -void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible) +void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible) { switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - SET_FLAG_FROM_TEST(ss->mvert[index].flag, !visible, ME_HIDE); - BKE_pbvh_vert_tag_update_normal(ss->pbvh, index); + case PBVH_FACES: { + bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh); + hide_vert[vertex.i] = visible; break; - case PBVH_BMESH: - BM_elem_flag_set(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN, !visible); + } + case PBVH_BMESH: { + BMVert *v = (BMVert *)vertex.i; + BM_elem_flag_set(v, BM_ELEM_HIDDEN, !visible); break; + } case PBVH_GRIDS: break; } } -bool SCULPT_vertex_visible_get(SculptSession *ss, int index) +bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { - case PBVH_FACES: - return !(ss->mvert[index].flag & ME_HIDE); + case PBVH_FACES: { + const bool *hide_vert = BKE_pbvh_get_vert_hide(ss->pbvh); + return hide_vert == NULL || !hide_vert[vertex.i]; + } case PBVH_BMESH: - return !BM_elem_flag_test(BM_vert_at_index(ss->bm, index), BM_ELEM_HIDDEN); + return !BM_elem_flag_test((BMVert *)vertex.i, BM_ELEM_HIDDEN); case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; BLI_bitmap **grid_hidden = BKE_pbvh_get_grid_visibility(ss->pbvh); if (grid_hidden && grid_hidden[grid_index]) { return !BLI_BITMAP_TEST(grid_hidden[grid_index], vertex_index); @@ -436,12 +444,12 @@ void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible) } } -bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index) +bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int j = 0; j < ss->pmap[vertex.i].count; j++) { if (ss->face_sets[vert_map->indices[j]] > 0) { return true; } @@ -456,12 +464,12 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index) return true; } -bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index) +bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int j = 0; j < ss->pmap[vertex.i].count; j++) { if (ss->face_sets[vert_map->indices[j]] < 0) { return false; } @@ -472,7 +480,7 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index) return true; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); return ss->face_sets[face_index] > 0; } @@ -480,12 +488,12 @@ bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index) return true; } -void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) +void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int j = 0; j < ss->pmap[index].count; j++) { + MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int j = 0; j < ss->pmap[vertex.i].count; j++) { if (ss->face_sets[vert_map->indices[j]] > 0) { ss->face_sets[vert_map->indices[j]] = abs(face_set); } @@ -495,7 +503,7 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) break; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); if (ss->face_sets[face_index] > 0) { ss->face_sets[face_index] = abs(face_set); @@ -505,13 +513,13 @@ void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set) } } -int SCULPT_vertex_face_set_get(SculptSession *ss, int index) +int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; + MeshElemMap *vert_map = &ss->pmap[vertex.i]; int face_set = 0; - for (int i = 0; i < ss->pmap[index].count; i++) { + for (int i = 0; i < ss->pmap[vertex.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] > face_set) { face_set = abs(ss->face_sets[vert_map->indices[i]]); } @@ -522,7 +530,7 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index) return 0; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); return ss->face_sets[face_index]; } @@ -530,12 +538,12 @@ int SCULPT_vertex_face_set_get(SculptSession *ss, int index) return 0; } -bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set) +bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - MeshElemMap *vert_map = &ss->pmap[index]; - for (int i = 0; i < ss->pmap[index].count; i++) { + MeshElemMap *vert_map = &ss->pmap[vertex.i]; + for (int i = 0; i < ss->pmap[vertex.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] == face_set) { return true; } @@ -546,7 +554,7 @@ bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set) return true; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; + const int grid_index = vertex.i / key->grid_area; const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, grid_index); return ss->face_sets[face_index] == face_set; } @@ -574,11 +582,11 @@ void SCULPT_visibility_sync_all_face_sets_to_vertices(Object *ob) } static void UNUSED_FUNCTION(sculpt_visibility_sync_vertex_to_face_sets)(SculptSession *ss, - int index) + PBVHVertRef vertex) { - MeshElemMap *vert_map = &ss->pmap[index]; - const bool visible = SCULPT_vertex_visible_get(ss, index); - for (int i = 0; i < ss->pmap[index].count; i++) { + MeshElemMap *vert_map = &ss->pmap[vertex.i]; + const bool visible = SCULPT_vertex_visible_get(ss, vertex); + for (int i = 0; i < ss->pmap[vertex.i].count; i++) { if (visible) { ss->face_sets[vert_map->indices[i]] = abs(ss->face_sets[vert_map->indices[i]]); } @@ -596,7 +604,7 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss) bool poly_visible = true; for (int l = 0; l < poly->totloop; l++) { MLoop *loop = &ss->mloop[poly->loopstart + l]; - if (!SCULPT_vertex_visible_get(ss, (int)loop->v)) { + if (!SCULPT_vertex_visible_get(ss, BKE_pbvh_make_vref(loop->v))) { poly_visible = false; } } @@ -659,18 +667,18 @@ static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss return true; } -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index) +bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - return sculpt_check_unique_face_set_in_base_mesh(ss, index); + return sculpt_check_unique_face_set_in_base_mesh(ss, vertex.i); } case PBVH_BMESH: return true; case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; const SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, .y = vertex_index / key->grid_size}; @@ -714,10 +722,12 @@ int SCULPT_face_set_next_available_get(SculptSession *ss) #define SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY 256 -static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neighbor_index) +static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, + PBVHVertRef neighbor, + int neighbor_index) { for (int i = 0; i < iter->size; i++) { - if (iter->neighbors[i] == neighbor_index) { + if (iter->neighbors[i].i == neighbor.i) { return; } } @@ -726,63 +736,74 @@ static void sculpt_vertex_neighbor_add(SculptVertexNeighborIter *iter, int neigh iter->capacity += SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; if (iter->neighbors == iter->neighbors_fixed) { - iter->neighbors = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); - memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(int) * iter->size); + iter->neighbors = MEM_mallocN(iter->capacity * sizeof(PBVHVertRef), "neighbor array"); + memcpy(iter->neighbors, iter->neighbors_fixed, sizeof(PBVHVertRef) * iter->size); } else { iter->neighbors = MEM_reallocN_id( - iter->neighbors, iter->capacity * sizeof(int), "neighbor array"); + iter->neighbors, iter->capacity * sizeof(PBVHVertRef), "neighbor array"); + } + + if (iter->neighbor_indices == iter->neighbor_indices_fixed) { + iter->neighbor_indices = MEM_mallocN(iter->capacity * sizeof(int), "neighbor array"); + memcpy(iter->neighbor_indices, iter->neighbor_indices_fixed, sizeof(int) * iter->size); + } + else { + iter->neighbor_indices = MEM_reallocN_id( + iter->neighbor_indices, iter->capacity * sizeof(int), "neighbor array"); } } - iter->neighbors[iter->size] = neighbor_index; + iter->neighbors[iter->size] = neighbor; + iter->neighbor_indices[iter->size] = neighbor_index; iter->size++; } -static void sculpt_vertex_neighbors_get_bmesh(SculptSession *ss, - int index, - SculptVertexNeighborIter *iter) +static void sculpt_vertex_neighbors_get_bmesh(PBVHVertRef vertex, SculptVertexNeighborIter *iter) { - BMVert *v = BM_vert_at_index(ss->bm, index); + BMVert *v = (BMVert *)vertex.i; BMIter liter; BMLoop *l; iter->size = 0; iter->num_duplicates = 0; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; + iter->neighbor_indices = iter->neighbor_indices_fixed; BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { const BMVert *adj_v[2] = {l->prev->v, l->next->v}; for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { const BMVert *v_other = adj_v[i]; - if (BM_elem_index_get(v_other) != (int)index) { - sculpt_vertex_neighbor_add(iter, BM_elem_index_get(v_other)); + if (v_other != v) { + sculpt_vertex_neighbor_add( + iter, BKE_pbvh_make_vref((intptr_t)v_other), BM_elem_index_get(v_other)); } } } } static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, - int index, + PBVHVertRef vertex, SculptVertexNeighborIter *iter) { - MeshElemMap *vert_map = &ss->pmap[index]; + MeshElemMap *vert_map = &ss->pmap[vertex.i]; iter->size = 0; iter->num_duplicates = 0; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; + iter->neighbor_indices = iter->neighbor_indices_fixed; - for (int i = 0; i < ss->pmap[index].count; i++) { + for (int i = 0; i < ss->pmap[vertex.i].count; i++) { if (ss->face_sets[vert_map->indices[i]] < 0) { /* Skip connectivity from hidden faces. */ continue; } const MPoly *p = &ss->mpoly[vert_map->indices[i]]; uint f_adj_v[2]; - if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) { + if (poly_get_adj_loops_from_vert(p, ss->mloop, vertex.i, f_adj_v) != -1) { for (int j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - if (f_adj_v[j] != index) { - sculpt_vertex_neighbor_add(iter, f_adj_v[j]); + if (f_adj_v[j] != vertex.i) { + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(f_adj_v[j]), f_adj_v[j]); } } } @@ -790,14 +811,17 @@ static void sculpt_vertex_neighbors_get_faces(SculptSession *ss, if (ss->fake_neighbors.use_fake_neighbors) { BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); - if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) { - sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]); + if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { + sculpt_vertex_neighbor_add( + iter, + BKE_pbvh_make_vref(ss->fake_neighbors.fake_neighbor_index[vertex.i]), + ss->fake_neighbors.fake_neighbor_index[vertex.i]); } } } static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, - const int index, + const PBVHVertRef vertex, const bool include_duplicates, SculptVertexNeighborIter *iter) { @@ -805,8 +829,8 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, * maybe provide coordinate and mask pointers directly rather than converting * back and forth between #CCGElem and global index. */ const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, @@ -819,17 +843,20 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, iter->num_duplicates = neighbors.num_duplicates; iter->capacity = SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY; iter->neighbors = iter->neighbors_fixed; + iter->neighbor_indices = iter->neighbor_indices_fixed; for (int i = 0; i < neighbors.size; i++) { - sculpt_vertex_neighbor_add(iter, - neighbors.coords[i].grid_index * key->grid_area + - neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x); + int v = neighbors.coords[i].grid_index * key->grid_area + + neighbors.coords[i].y * key->grid_size + neighbors.coords[i].x; + + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); } if (ss->fake_neighbors.use_fake_neighbors) { BLI_assert(ss->fake_neighbors.fake_neighbor_index != NULL); - if (ss->fake_neighbors.fake_neighbor_index[index] != FAKE_NEIGHBOR_NONE) { - sculpt_vertex_neighbor_add(iter, ss->fake_neighbors.fake_neighbor_index[index]); + if (ss->fake_neighbors.fake_neighbor_index[vertex.i] != FAKE_NEIGHBOR_NONE) { + int v = ss->fake_neighbors.fake_neighbor_index[vertex.i]; + sculpt_vertex_neighbor_add(iter, BKE_pbvh_make_vref(v), v); } } @@ -839,19 +866,19 @@ static void sculpt_vertex_neighbors_get_grids(SculptSession *ss, } void SCULPT_vertex_neighbors_get(SculptSession *ss, - const int index, + const PBVHVertRef vertex, const bool include_duplicates, SculptVertexNeighborIter *iter) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: - sculpt_vertex_neighbors_get_faces(ss, index, iter); + sculpt_vertex_neighbors_get_faces(ss, vertex, iter); return; case PBVH_BMESH: - sculpt_vertex_neighbors_get_bmesh(ss, index, iter); + sculpt_vertex_neighbors_get_bmesh(vertex, iter); return; case PBVH_GRIDS: - sculpt_vertex_neighbors_get_grids(ss, index, include_duplicates, iter); + sculpt_vertex_neighbors_get_grids(ss, vertex, include_duplicates, iter); return; } } @@ -862,24 +889,24 @@ static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, c return BLI_BITMAP_TEST(ss->vertex_info.boundary, index); } -bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index) +bool SCULPT_vertex_is_boundary(const SculptSession *ss, const PBVHVertRef vertex) { switch (BKE_pbvh_type(ss->pbvh)) { case PBVH_FACES: { - if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) { + if (!SCULPT_vertex_all_face_sets_visible_get(ss, vertex)) { return true; } - return sculpt_check_boundary_vertex_in_base_mesh(ss, index); + return sculpt_check_boundary_vertex_in_base_mesh(ss, vertex.i); } case PBVH_BMESH: { - BMVert *v = BM_vert_at_index(ss->bm, index); + BMVert *v = (BMVert *)vertex.i; return BM_vert_is_boundary(v); } case PBVH_GRIDS: { const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); - const int grid_index = index / key->grid_area; - const int vertex_index = index - grid_index * key->grid_area; + const int grid_index = vertex.i / key->grid_area; + const int vertex_index = vertex.i - grid_index * key->grid_area; const SubdivCCGCoord coord = {.grid_index = grid_index, .x = vertex_index % key->grid_size, .y = vertex_index / key->grid_size}; @@ -940,7 +967,7 @@ bool SCULPT_check_vertex_pivot_symmetry(const float vco[3], const float pco[3], } typedef struct NearestVertexTLSData { - int nearest_vertex_index; + PBVHVertRef nearest_vertex; float nearest_vertex_distance_squared; } NearestVertexTLSData; @@ -957,7 +984,7 @@ static void do_nearest_vertex_get_task_cb(void *__restrict userdata, float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { - nvtd->nearest_vertex_index = vd.index; + nvtd->nearest_vertex = vd.vertex; nvtd->nearest_vertex_distance_squared = distance_squared; } } @@ -970,17 +997,17 @@ static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata), { NearestVertexTLSData *join = chunk_join; NearestVertexTLSData *nvtd = chunk; - if (join->nearest_vertex_index == -1) { - join->nearest_vertex_index = nvtd->nearest_vertex_index; + if (join->nearest_vertex.i == PBVH_REF_NONE) { + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { - join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } } -int SCULPT_nearest_vertex_get( +PBVHVertRef SCULPT_nearest_vertex_get( Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original) { SculptSession *ss = ob->sculpt; @@ -995,7 +1022,7 @@ int SCULPT_nearest_vertex_get( }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); if (totnode == 0) { - return -1; + return BKE_pbvh_make_vref(PBVH_REF_NONE); } SculptThreadedTaskData task_data = { @@ -1007,7 +1034,7 @@ int SCULPT_nearest_vertex_get( copy_v3_v3(task_data.nearest_vertex_search_co, co); NearestVertexTLSData nvtd; - nvtd.nearest_vertex_index = -1; + nvtd.nearest_vertex.i = PBVH_REF_NONE; nvtd.nearest_vertex_distance_squared = FLT_MAX; TaskParallelSettings settings; @@ -1019,7 +1046,7 @@ int SCULPT_nearest_vertex_get( MEM_SAFE_FREE(nodes); - return nvtd.nearest_vertex_index; + return nvtd.nearest_vertex; } bool SCULPT_is_symmetry_iteration_valid(char i, char symm) @@ -1074,23 +1101,27 @@ void SCULPT_floodfill_init(SculptSession *ss, SculptFloodFill *flood) int vertex_count = SCULPT_vertex_count_get(ss); SCULPT_vertex_random_access_ensure(ss); - flood->queue = BLI_gsqueue_new(sizeof(int)); + flood->queue = BLI_gsqueue_new(sizeof(intptr_t)); flood->visited_vertices = BLI_BITMAP_NEW(vertex_count, "visited vertices"); } -void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index) +void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex) { - BLI_gsqueue_push(flood->queue, &index); + BLI_gsqueue_push(flood->queue, &vertex); } -void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index) +void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex) { - BLI_gsqueue_push(flood->queue, &index); - BLI_BITMAP_ENABLE(flood->visited_vertices, index); + BLI_gsqueue_push(flood->queue, &vertex); + BLI_BITMAP_ENABLE(flood->visited_vertices, vertex.i); } -void SCULPT_floodfill_add_initial_with_symmetry( - Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius) +void SCULPT_floodfill_add_initial_with_symmetry(Sculpt *sd, + Object *ob, + SculptSession *ss, + SculptFloodFill *flood, + PBVHVertRef vertex, + float radius) { /* Add active vertex and symmetric vertices to the queue. */ const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -1098,18 +1129,19 @@ void SCULPT_floodfill_add_initial_with_symmetry( if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { continue; } - int v = -1; + PBVHVertRef v = {PBVH_REF_NONE}; + if (i == 0) { - v = index; + v = vertex; } else if (radius > 0.0f) { float radius_squared = (radius == FLT_MAX) ? FLT_MAX : radius * radius; float location[3]; - flip_v3_v3(location, SCULPT_vertex_co_get(ss, index), i); + flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i); v = SCULPT_nearest_vertex_get(sd, ob, location, radius_squared, false); } - if (v != -1) { + if (v.i != PBVH_REF_NONE) { SCULPT_floodfill_add_initial(flood, v); } } @@ -1124,7 +1156,9 @@ void SCULPT_floodfill_add_active( if (!SCULPT_is_symmetry_iteration_valid(i, symm)) { continue; } - int v = -1; + + PBVHVertRef v = {PBVH_REF_NONE}; + if (i == 0) { v = SCULPT_active_vertex_get(ss); } @@ -1134,26 +1168,31 @@ void SCULPT_floodfill_add_active( v = SCULPT_nearest_vertex_get(sd, ob, location, radius, false); } - if (v != -1) { + if (v.i != PBVH_REF_NONE) { SCULPT_floodfill_add_initial(flood, v); } } } -void SCULPT_floodfill_execute( - SculptSession *ss, - SculptFloodFill *flood, - bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata), - void *userdata) +void SCULPT_floodfill_execute(SculptSession *ss, + SculptFloodFill *flood, + bool (*func)(SculptSession *ss, + PBVHVertRef from_v, + PBVHVertRef to_v, + bool is_duplicate, + void *userdata), + void *userdata) { while (!BLI_gsqueue_is_empty(flood->queue)) { - int from_v; + PBVHVertRef from_v; + BLI_gsqueue_pop(flood->queue, &from_v); SculptVertexNeighborIter ni; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - const int to_v = ni.index; + const PBVHVertRef to_v = ni.vertex; + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); - if (BLI_BITMAP_TEST(flood->visited_vertices, to_v)) { + if (BLI_BITMAP_TEST(flood->visited_vertices, to_v_i)) { continue; } @@ -1161,7 +1200,7 @@ void SCULPT_floodfill_execute( continue; } - BLI_BITMAP_ENABLE(flood->visited_vertices, to_v); + BLI_BITMAP_ENABLE(flood->visited_vertices, BKE_pbvh_vertex_to_index(ss->pbvh, to_v)); if (func(ss, from_v, to_v, ni.is_duplicate, userdata)) { BLI_gsqueue_push(flood->queue, &to_v); @@ -1408,14 +1447,14 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, copy_v3_v3(vd.fno, orig_data.no); } if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } else if (orig_data.unode->type == SCULPT_UNDO_MASK) { *vd.mask = orig_data.mask; } else if (orig_data.unode->type == SCULPT_UNDO_COLOR) { - SCULPT_vertex_color_set(ss, vd.index, orig_data.col); + SCULPT_vertex_color_set(ss, vd.vertex, orig_data.col); } } BKE_pbvh_vertex_iter_end; @@ -2359,12 +2398,12 @@ static float brush_strength(const Sculpt *sd, float SCULPT_brush_strength_factor(SculptSession *ss, const Brush *br, const float brush_point[3], - const float len, + float len, const float vno[3], const float fno[3], - const float mask, - const int vertex_index, - const int thread_id) + float mask, + const PBVHVertRef vertex, + int thread_id) { StrokeCache *cache = ss->cache; const Scene *scene = cache->vc->scene; @@ -2448,7 +2487,7 @@ float SCULPT_brush_strength_factor(SculptSession *ss, avg *= 1.0f - mask; /* Auto-masking. */ - avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex_index); + avg *= SCULPT_automasking_factor_get(cache->automasking, ss, vertex); return avg; } @@ -2817,7 +2856,7 @@ typedef struct { float depth; bool original; - int active_vertex_index; + PBVHVertRef active_vertex; float *face_normal; int active_face_grid_index; @@ -3037,13 +3076,13 @@ static void do_gravity_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -4742,7 +4781,7 @@ static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) srd->ray_normal, &srd->isect_precalc, &srd->depth, - &srd->active_vertex_index, + &srd->active_vertex, &srd->active_face_grid_index, srd->face_normal)) { srd->hit = true; @@ -4876,7 +4915,7 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, } /* Update the active vertex of the SculptSession. */ - ss->active_vertex_index = srd.active_vertex_index; + ss->active_vertex = srd.active_vertex; SCULPT_vertex_random_access_ensure(ss); copy_v3_v3(out->active_vertex_co, SCULPT_active_vertex_co_get(ss)); @@ -5621,7 +5660,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot) ot->cancel = sculpt_brush_stroke_cancel; /* Flags (sculpt does own undo? (ton)). */ - ot->flag = OPTYPE_BLOCKING; + ot->flag = OPTYPE_BLOCKING | OPTYPE_REGISTER | OPTYPE_UNDO; /* Properties. */ @@ -5661,10 +5700,10 @@ enum { SCULPT_TOPOLOGY_ID_DEFAULT, }; -static int SCULPT_vertex_get_connected_component(SculptSession *ss, int index) +static int SCULPT_vertex_get_connected_component(SculptSession *ss, PBVHVertRef vertex) { if (ss->vertex_info.connected_component) { - return ss->vertex_info.connected_component[index]; + return ss->vertex_info.connected_component[vertex.i]; } return SCULPT_TOPOLOGY_ID_DEFAULT; } @@ -5681,8 +5720,11 @@ static void SCULPT_fake_neighbor_init(SculptSession *ss, const float max_dist) ss->fake_neighbors.current_max_distance = max_dist; } -static void SCULPT_fake_neighbor_add(SculptSession *ss, int v_index_a, int v_index_b) +static void SCULPT_fake_neighbor_add(SculptSession *ss, PBVHVertRef v_a, PBVHVertRef v_b) { + int v_index_a = BKE_pbvh_vertex_to_index(ss->pbvh, v_a); + int v_index_b = BKE_pbvh_vertex_to_index(ss->pbvh, v_b); + if (ss->fake_neighbors.fake_neighbor_index[v_index_a] == FAKE_NEIGHBOR_NONE) { ss->fake_neighbors.fake_neighbor_index[v_index_a] = v_index_b; ss->fake_neighbors.fake_neighbor_index[v_index_b] = v_index_a; @@ -5695,7 +5737,7 @@ static void sculpt_pose_fake_neighbors_free(SculptSession *ss) } typedef struct NearestVertexFakeNeighborTLSData { - int nearest_vertex_index; + PBVHVertRef nearest_vertex; float nearest_vertex_distance_squared; int current_topology_id; } NearestVertexFakeNeighborTLSData; @@ -5710,13 +5752,13 @@ static void do_fake_neighbor_search_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.index); + int vd_topology_id = SCULPT_vertex_get_connected_component(ss, vd.vertex); if (vd_topology_id != nvtd->current_topology_id && ss->fake_neighbors.fake_neighbor_index[vd.index] == FAKE_NEIGHBOR_NONE) { float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co); if (distance_squared < nvtd->nearest_vertex_distance_squared && distance_squared < data->max_distance_squared) { - nvtd->nearest_vertex_index = vd.index; + nvtd->nearest_vertex = vd.vertex; nvtd->nearest_vertex_distance_squared = distance_squared; } } @@ -5730,17 +5772,20 @@ static void fake_neighbor_search_reduce(const void *__restrict UNUSED(userdata), { NearestVertexFakeNeighborTLSData *join = chunk_join; NearestVertexFakeNeighborTLSData *nvtd = chunk; - if (join->nearest_vertex_index == -1) { - join->nearest_vertex_index = nvtd->nearest_vertex_index; + if (join->nearest_vertex.i == PBVH_REF_NONE) { + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } else if (nvtd->nearest_vertex_distance_squared < join->nearest_vertex_distance_squared) { - join->nearest_vertex_index = nvtd->nearest_vertex_index; + join->nearest_vertex = nvtd->nearest_vertex; join->nearest_vertex_distance_squared = nvtd->nearest_vertex_distance_squared; } } -static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, float max_distance) +static PBVHVertRef SCULPT_fake_neighbor_search(Sculpt *sd, + Object *ob, + const PBVHVertRef vertex, + float max_distance) { SculptSession *ss = ob->sculpt; PBVHNode **nodes = NULL; @@ -5750,12 +5795,12 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, .sd = sd, .radius_squared = max_distance * max_distance, .original = false, - .center = SCULPT_vertex_co_get(ss, index), + .center = SCULPT_vertex_co_get(ss, vertex), }; BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode); if (totnode == 0) { - return -1; + return BKE_pbvh_make_vref(PBVH_REF_NONE); } SculptThreadedTaskData task_data = { @@ -5765,12 +5810,12 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, .max_distance_squared = max_distance * max_distance, }; - copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(task_data.nearest_vertex_search_co, SCULPT_vertex_co_get(ss, vertex)); NearestVertexFakeNeighborTLSData nvtd; - nvtd.nearest_vertex_index = -1; + nvtd.nearest_vertex.i = -1; nvtd.nearest_vertex_distance_squared = FLT_MAX; - nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, index); + nvtd.current_topology_id = SCULPT_vertex_get_connected_component(ss, vertex); TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); @@ -5781,19 +5826,26 @@ static int SCULPT_fake_neighbor_search(Sculpt *sd, Object *ob, const int index, MEM_SAFE_FREE(nodes); - return nvtd.nearest_vertex_index; + return nvtd.nearest_vertex; } typedef struct SculptTopologyIDFloodFillData { int next_id; } SculptTopologyIDFloodFillData; -static bool SCULPT_connected_components_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool SCULPT_connected_components_floodfill_cb(SculptSession *ss, + PBVHVertRef from_v, + PBVHVertRef to_v, + bool UNUSED(is_duplicate), + void *userdata) { SculptTopologyIDFloodFillData *data = userdata; - ss->vertex_info.connected_component[from_v] = data->next_id; - ss->vertex_info.connected_component[to_v] = data->next_id; + + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + + ss->vertex_info.connected_component[from_v_i] = data->next_id; + ss->vertex_info.connected_component[to_v_i] = data->next_id; return true; } @@ -5817,10 +5869,12 @@ void SCULPT_connected_components_ensure(Object *ob) int next_id = 0; for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (ss->vertex_info.connected_component[i] == SCULPT_TOPOLOGY_ID_NONE) { SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); - SCULPT_floodfill_add_initial(&flood, i); + SCULPT_floodfill_add_initial(&flood, vertex); SculptTopologyIDFloodFillData data; data.next_id = next_id; SCULPT_floodfill_execute(ss, &flood, SCULPT_connected_components_floodfill_cb, &data); @@ -5878,12 +5932,12 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) SCULPT_fake_neighbor_init(ss, max_dist); for (int i = 0; i < totvert; i++) { - const int from_v = i; + const PBVHVertRef from_v = BKE_pbvh_index_to_vertex(ss->pbvh, i); /* This vertex does not have a fake neighbor yet, search one for it. */ - if (ss->fake_neighbors.fake_neighbor_index[from_v] == FAKE_NEIGHBOR_NONE) { - const int to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist); - if (to_v != -1) { + if (ss->fake_neighbors.fake_neighbor_index[i] == FAKE_NEIGHBOR_NONE) { + const PBVHVertRef to_v = SCULPT_fake_neighbor_search(sd, ob, from_v, max_dist); + if (to_v.i != PBVH_REF_NONE) { /* Add the fake neighbor if available. */ SCULPT_fake_neighbor_add(ss, from_v, to_v); } diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index bb101717c9b..a9fe8cc4b2f 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -114,16 +114,21 @@ static bool SCULPT_automasking_needs_factors_cache(const Sculpt *sd, const Brush return false; } -float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, int vert) +float SCULPT_automasking_factor_get(AutomaskingCache *automasking, + SculptSession *ss, + PBVHVertRef vert) { if (!automasking) { return 1.0f; } + + int index = BKE_pbvh_vertex_to_index(ss->pbvh, vert); + /* If the cache is initialized with valid info, use the cache. This is used when the * automasking information can't be computed in real time per vertex and needs to be * initialized for the whole mesh when the stroke starts. */ if (automasking->factor) { - return automasking->factor[vert]; + return automasking->factor[index]; } if (automasking->settings.flags & BRUSH_AUTOMASKING_FACE_SETS) { @@ -178,13 +183,18 @@ struct AutomaskFloodFillData { char symm; }; -static bool automask_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool automask_floodfill_cb(SculptSession *ss, + PBVHVertRef from_v, + PBVHVertRef to_v, + bool UNUSED(is_duplicate), + void *userdata) { AutomaskFloodFillData *data = (AutomaskFloodFillData *)userdata; + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); - data->automask_factor[to_v] = 1.0f; - data->automask_factor[from_v] = 1.0f; + data->automask_factor[to_v_i] = 1.0f; + data->automask_factor[from_v_i] = 1.0f; return (!data->use_radius || SCULPT_is_vertex_inside_brush_radius_symm( SCULPT_vertex_co_get(ss, to_v), data->location, data->radius, data->symm)); @@ -243,7 +253,9 @@ static float *sculpt_face_sets_automasking_init(Sculpt *sd, Object *ob, float *a int tot_vert = SCULPT_vertex_count_get(ss); int active_face_set = SCULPT_active_face_set_get(ss); for (int i : IndexRange(tot_vert)) { - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { automask_factor[i] *= 0.0f; } } @@ -269,15 +281,17 @@ float *SCULPT_boundary_automasking_init(Object *ob, int *edge_distance = (int *)MEM_callocN(sizeof(int) * totvert, "automask_factor"); for (int i : IndexRange(totvert)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + edge_distance[i] = EDGE_DISTANCE_INF; switch (mode) { case AUTOMASK_INIT_BOUNDARY_EDGES: - if (SCULPT_vertex_is_boundary(ss, i)) { + if (SCULPT_vertex_is_boundary(ss, vertex)) { edge_distance[i] = 0; } break; case AUTOMASK_INIT_BOUNDARY_FACE_SETS: - if (!SCULPT_vertex_has_unique_face_set(ss, i)) { + if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) { edge_distance[i] = 0; } break; @@ -286,11 +300,13 @@ float *SCULPT_boundary_automasking_init(Object *ob, for (int propagation_it : IndexRange(propagation_steps)) { for (int i : IndexRange(totvert)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (edge_distance[i] != EDGE_DISTANCE_INF) { continue; } SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { if (edge_distance[ni.index] == propagation_it) { edge_distance[i] = propagation_it + 1; } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index 390fcb9648a..8d08c338b93 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -46,32 +46,38 @@ #define BOUNDARY_STEPS_NONE -1 typedef struct BoundaryInitialVertexFloodFillData { - int initial_vertex; + PBVHVertRef initial_vertex; + int initial_vertex_i; int boundary_initial_vertex_steps; - int boundary_initial_vertex; + PBVHVertRef boundary_initial_vertex; + int boundary_initial_vertex_i; int *floodfill_steps; float radius_sq; } BoundaryInitialVertexFloodFillData; static bool boundary_initial_vertex_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { BoundaryInitialVertexFloodFillData *data = userdata; + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + if (!SCULPT_vertex_visible_get(ss, to_v)) { return false; } if (!is_duplicate) { - data->floodfill_steps[to_v] = data->floodfill_steps[from_v] + 1; + data->floodfill_steps[to_v_i] = data->floodfill_steps[from_v_i] + 1; } else { - data->floodfill_steps[to_v] = data->floodfill_steps[from_v]; + data->floodfill_steps[to_v_i] = data->floodfill_steps[from_v_i]; } if (SCULPT_vertex_is_boundary(ss, to_v)) { - if (data->floodfill_steps[to_v] < data->boundary_initial_vertex_steps) { - data->boundary_initial_vertex_steps = data->floodfill_steps[to_v]; + if (data->floodfill_steps[to_v_i] < data->boundary_initial_vertex_steps) { + data->boundary_initial_vertex_steps = data->floodfill_steps[to_v_i]; + data->boundary_initial_vertex_i = to_v_i; data->boundary_initial_vertex = to_v; } } @@ -83,9 +89,9 @@ static bool boundary_initial_vertex_floodfill_cb( /* From a vertex index anywhere in the mesh, returns the closest vertex in a mesh boundary inside * the given radius, if it exists. */ -static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, - const int initial_vertex, - const float radius) +static PBVHVertRef sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, + const PBVHVertRef initial_vertex, + const float radius) { if (SCULPT_vertex_is_boundary(ss, initial_vertex)) { @@ -98,7 +104,7 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, BoundaryInitialVertexFloodFillData fdata = { .initial_vertex = initial_vertex, - .boundary_initial_vertex = BOUNDARY_VERTEX_NONE, + .boundary_initial_vertex = {BOUNDARY_VERTEX_NONE}, .boundary_initial_vertex_steps = INT_MAX, .radius_sq = radius * radius, }; @@ -119,12 +125,14 @@ static int sculpt_boundary_get_closest_boundary_vertex(SculptSession *ss, static int BOUNDARY_INDICES_BLOCK_SIZE = 300; static void sculpt_boundary_index_add(SculptBoundary *boundary, + const PBVHVertRef new_vertex, const int new_index, const float distance, GSet *included_vertices) { - boundary->vertices[boundary->num_vertices] = new_index; + boundary->vertices[boundary->num_vertices] = new_vertex; + if (boundary->distance) { boundary->distance[new_index] = distance; } @@ -135,11 +143,13 @@ static void sculpt_boundary_index_add(SculptBoundary *boundary, if (boundary->num_vertices >= boundary->vertices_capacity) { boundary->vertices_capacity += BOUNDARY_INDICES_BLOCK_SIZE; boundary->vertices = MEM_reallocN_id( - boundary->vertices, boundary->vertices_capacity * sizeof(int), "boundary indices"); + boundary->vertices, boundary->vertices_capacity * sizeof(PBVHVertRef), "boundary indices"); } }; -static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int v1, const int v2) +static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, + const PBVHVertRef v1, + const PBVHVertRef v2) { boundary->edges[boundary->num_edges].v1 = v1; @@ -159,7 +169,7 @@ static void sculpt_boundary_preview_edge_add(SculptBoundary *boundary, const int * as well as to check if the initial vertex is valid. */ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, - const int initial_vertex) + const PBVHVertRef initial_vertex) { if (!SCULPT_vertex_visible_get(ss, initial_vertex)) { @@ -170,9 +180,9 @@ static bool sculpt_boundary_is_vertex_in_editable_boundary(SculptSession *ss, int boundary_vertex_count = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, initial_vertex, ni) { - if (SCULPT_vertex_visible_get(ss, ni.index)) { + if (SCULPT_vertex_visible_get(ss, ni.vertex)) { neighbor_count++; - if (SCULPT_vertex_is_boundary(ss, ni.index)) { + if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { boundary_vertex_count++; } } @@ -202,13 +212,16 @@ typedef struct BoundaryFloodFillData { GSet *included_vertices; EdgeSet *preview_edges; - int last_visited_vertex; + PBVHVertRef last_visited_vertex; } BoundaryFloodFillData; static bool boundary_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + BoundaryFloodFillData *data = userdata; SculptBoundary *boundary = data->boundary; if (!SCULPT_vertex_is_boundary(ss, to_v)) { @@ -217,9 +230,10 @@ static bool boundary_floodfill_cb( const float edge_len = len_v3v3(SCULPT_vertex_co_get(ss, from_v), SCULPT_vertex_co_get(ss, to_v)); const float distance_boundary_to_dst = boundary->distance ? - boundary->distance[from_v] + edge_len : + boundary->distance[from_v_i] + edge_len : 0.0f; - sculpt_boundary_index_add(boundary, to_v, distance_boundary_to_dst, data->included_vertices); + sculpt_boundary_index_add( + boundary, to_v, to_v_i, distance_boundary_to_dst, data->included_vertices); if (!is_duplicate) { sculpt_boundary_preview_edge_add(boundary, from_v, to_v); } @@ -229,12 +243,13 @@ static bool boundary_floodfill_cb( static void sculpt_boundary_indices_init(SculptSession *ss, SculptBoundary *boundary, const bool init_boundary_distances, - const int initial_boundary_index) + const PBVHVertRef initial_boundary_vertex) { const int totvert = SCULPT_vertex_count_get(ss); boundary->vertices = MEM_malloc_arrayN( - BOUNDARY_INDICES_BLOCK_SIZE, sizeof(int), "boundary indices"); + BOUNDARY_INDICES_BLOCK_SIZE, sizeof(PBVHVertRef), "boundary indices"); + if (init_boundary_distances) { boundary->distance = MEM_calloc_arrayN(totvert, sizeof(float), "boundary distances"); } @@ -245,16 +260,21 @@ static void sculpt_boundary_indices_init(SculptSession *ss, SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); - boundary->initial_vertex = initial_boundary_index; + int initial_boundary_index = BKE_pbvh_vertex_to_index(ss->pbvh, initial_boundary_vertex); + + boundary->initial_vertex = initial_boundary_vertex; + boundary->initial_vertex_i = initial_boundary_index; + copy_v3_v3(boundary->initial_vertex_position, SCULPT_vertex_co_get(ss, boundary->initial_vertex)); - sculpt_boundary_index_add(boundary, initial_boundary_index, 0.0f, included_vertices); - SCULPT_floodfill_add_initial(&flood, initial_boundary_index); + sculpt_boundary_index_add( + boundary, initial_boundary_vertex, initial_boundary_index, 0.0f, included_vertices); + SCULPT_floodfill_add_initial(&flood, boundary->initial_vertex); BoundaryFloodFillData fdata = { .boundary = boundary, .included_vertices = included_vertices, - .last_visited_vertex = BOUNDARY_VERTEX_NONE, + .last_visited_vertex = {BOUNDARY_VERTEX_NONE}, }; @@ -262,13 +282,13 @@ static void sculpt_boundary_indices_init(SculptSession *ss, SCULPT_floodfill_free(&flood); /* Check if the boundary loops into itself and add the extra preview edge to close the loop. */ - if (fdata.last_visited_vertex != BOUNDARY_VERTEX_NONE && + if (fdata.last_visited_vertex.i != BOUNDARY_VERTEX_NONE && sculpt_boundary_is_vertex_in_editable_boundary(ss, fdata.last_visited_vertex)) { SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, fdata.last_visited_vertex, ni) { if (BLI_gset_haskey(included_vertices, POINTER_FROM_INT(ni.index)) && - sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.index)) { - sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.index); + sculpt_boundary_is_vertex_in_editable_boundary(ss, ni.vertex)) { + sculpt_boundary_preview_edge_add(boundary, fdata.last_visited_vertex, ni.vertex); boundary->forms_loop = true; } } @@ -286,7 +306,7 @@ static void sculpt_boundary_indices_init(SculptSession *ss, */ static void sculpt_boundary_edit_data_init(SculptSession *ss, SculptBoundary *boundary, - const int initial_vertex, + const PBVHVertRef initial_vertex, const float radius) { const int totvert = SCULPT_vertex_count_get(ss); @@ -297,19 +317,22 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, totvert, sizeof(SculptBoundaryEditInfo), "Boundary edit info"); for (int i = 0; i < totvert; i++) { - boundary->edit_info[i].original_vertex = BOUNDARY_VERTEX_NONE; + boundary->edit_info[i].original_vertex_i = BOUNDARY_VERTEX_NONE; boundary->edit_info[i].num_propagation_steps = BOUNDARY_STEPS_NONE; } - GSQueue *current_iteration = BLI_gsqueue_new(sizeof(int)); - GSQueue *next_iteration = BLI_gsqueue_new(sizeof(int)); + GSQueue *current_iteration = BLI_gsqueue_new(sizeof(PBVHVertRef)); + GSQueue *next_iteration = BLI_gsqueue_new(sizeof(PBVHVertRef)); /* Initialized the first iteration with the vertices already in the boundary. This is propagation * step 0. */ BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices"); for (int i = 0; i < boundary->num_vertices; i++) { - boundary->edit_info[boundary->vertices[i]].original_vertex = boundary->vertices[i]; - boundary->edit_info[boundary->vertices[i]].num_propagation_steps = 0; + int index = BKE_pbvh_vertex_to_index(ss->pbvh, boundary->vertices[i]); + + boundary->edit_info[index].original_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, + boundary->vertices[i]); + boundary->edit_info[index].num_propagation_steps = 0; /* This ensures that all duplicate vertices in the boundary have the same original_vertex * index, so the deformation for them will be the same. */ @@ -317,7 +340,8 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, SculptVertexNeighborIter ni_duplis; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, boundary->vertices[i], ni_duplis) { if (ni_duplis.is_duplicate) { - boundary->edit_info[ni_duplis.index].original_vertex = boundary->vertices[i]; + boundary->edit_info[ni_duplis.index].original_vertex_i = BKE_pbvh_vertex_to_index( + ss->pbvh, boundary->vertices[i]); } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); @@ -338,31 +362,33 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, } while (!BLI_gsqueue_is_empty(current_iteration)) { - int from_v; + PBVHVertRef from_v; BLI_gsqueue_pop(current_iteration, &from_v); + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + SculptVertexNeighborIter ni; SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { - const bool is_visible = SCULPT_vertex_visible_get(ss, ni.index); + const bool is_visible = SCULPT_vertex_visible_get(ss, ni.vertex); if (!is_visible || boundary->edit_info[ni.index].num_propagation_steps != BOUNDARY_STEPS_NONE) { continue; } - boundary->edit_info[ni.index].original_vertex = - boundary->edit_info[from_v].original_vertex; + boundary->edit_info[ni.index].original_vertex_i = + boundary->edit_info[from_v_i].original_vertex_i; BLI_BITMAP_ENABLE(visited_vertices, ni.index); if (ni.is_duplicate) { /* Grids duplicates handling. */ boundary->edit_info[ni.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps; + boundary->edit_info[from_v_i].num_propagation_steps; } else { boundary->edit_info[ni.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps + 1; + boundary->edit_info[from_v_i].num_propagation_steps + 1; - BLI_gsqueue_push(next_iteration, &ni.index); + BLI_gsqueue_push(next_iteration, &ni.vertex); /* When copying the data to the neighbor for the next iteration, it has to be copied to * all its duplicates too. This is because it is not possible to know if the updated @@ -370,12 +396,12 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, * copy the data in the from_v neighbor iterator. */ if (has_duplicates) { SculptVertexNeighborIter ni_duplis; - SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.index, ni_duplis) { + SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN (ss, ni.vertex, ni_duplis) { if (ni_duplis.is_duplicate) { - boundary->edit_info[ni_duplis.index].original_vertex = - boundary->edit_info[from_v].original_vertex; + boundary->edit_info[ni_duplis.index].original_vertex_i = + boundary->edit_info[from_v_i].original_vertex_i; boundary->edit_info[ni_duplis.index].num_propagation_steps = - boundary->edit_info[from_v].num_propagation_steps + 1; + boundary->edit_info[from_v_i].num_propagation_steps + 1; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni_duplis); @@ -383,11 +409,12 @@ static void sculpt_boundary_edit_data_init(SculptSession *ss, /* Check the distance using the vertex that was propagated from the initial vertex that * was used to initialize the boundary. */ - if (boundary->edit_info[from_v].original_vertex == initial_vertex) { - boundary->pivot_vertex = ni.index; - copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.index)); + if (boundary->edit_info[from_v_i].original_vertex_i == + BKE_pbvh_vertex_to_index(ss->pbvh, initial_vertex)) { + boundary->pivot_vertex = ni.vertex; + copy_v3_v3(boundary->initial_pivot_position, SCULPT_vertex_co_get(ss, ni.vertex)); accum_distance += len_v3v3(SCULPT_vertex_co_get(ss, from_v), - SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_vertex_co_get(ss, ni.vertex)); } } } @@ -427,7 +454,8 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, brush, boundary->edit_info[i].num_propagation_steps, boundary->max_propagation_steps); } - if (boundary->edit_info[i].original_vertex == boundary->initial_vertex) { + if (boundary->edit_info[i].original_vertex_i == + BKE_pbvh_vertex_to_index(ss->pbvh, boundary->initial_vertex)) { /* All vertices that are propagated from the original vertex won't be affected by the * boundary falloff, so there is no need to calculate anything else. */ continue; @@ -439,7 +467,7 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, continue; } - const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex]; + const float boundary_distance = boundary->distance[boundary->edit_info[i].original_vertex_i]; float falloff_distance = 0.0f; float direction = 1.0f; @@ -473,22 +501,22 @@ static void sculpt_boundary_falloff_factor_init(SculptSession *ss, SculptBoundary *SCULPT_boundary_data_init(Object *object, Brush *brush, - const int initial_vertex, + const PBVHVertRef initial_vertex, const float radius) { SculptSession *ss = object->sculpt; - if (initial_vertex == BOUNDARY_VERTEX_NONE) { + if (initial_vertex.i == PBVH_REF_NONE) { return NULL; } SCULPT_vertex_random_access_ensure(ss); SCULPT_boundary_info_ensure(object); - const int boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex( + const PBVHVertRef boundary_initial_vertex = sculpt_boundary_get_closest_boundary_vertex( ss, initial_vertex, radius); - if (boundary_initial_vertex == BOUNDARY_VERTEX_NONE) { + if (boundary_initial_vertex.i == BOUNDARY_VERTEX_NONE) { return NULL; } @@ -539,17 +567,22 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { continue; } + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float dir[3]; float normal[3]; - SCULPT_vertex_normal_get(ss, i, normal); - sub_v3_v3v3(dir, - SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, i)); + SCULPT_vertex_normal_get(ss, vertex, normal); + sub_v3_v3v3( + dir, + SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), + SCULPT_vertex_co_get(ss, vertex)); cross_v3_v3v3( - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex], dir, normal); - normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); - copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex], - SCULPT_vertex_co_get(ss, i)); + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i], dir, normal); + normalize_v3(boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); + copy_v3_v3(boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i], + SCULPT_vertex_co_get(ss, vertex)); } for (int i = 0; i < totvert; i++) { @@ -557,9 +590,9 @@ static void sculpt_boundary_bend_data_init(SculptSession *ss, SculptBoundary *bo continue; } copy_v3_v3(boundary->bend.pivot_positions[i], - boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex]); + boundary->bend.pivot_positions[boundary->edit_info[i].original_vertex_i]); copy_v3_v3(boundary->bend.pivot_rotation_axis[i], - boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex]); + boundary->bend.pivot_rotation_axis[boundary->edit_info[i].original_vertex_i]); } } @@ -572,10 +605,12 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b if (boundary->edit_info[i].num_propagation_steps != boundary->max_propagation_steps) { continue; } - sub_v3_v3v3(boundary->slide.directions[boundary->edit_info[i].original_vertex], - SCULPT_vertex_co_get(ss, boundary->edit_info[i].original_vertex), - SCULPT_vertex_co_get(ss, i)); - normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex]); + sub_v3_v3v3( + boundary->slide.directions[boundary->edit_info[i].original_vertex_i], + SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, boundary->edit_info[i].original_vertex_i)), + SCULPT_vertex_co_get(ss, BKE_pbvh_index_to_vertex(ss->pbvh, i))); + normalize_v3(boundary->slide.directions[boundary->edit_info[i].original_vertex_i]); } for (int i = 0; i < totvert; i++) { @@ -583,7 +618,7 @@ static void sculpt_boundary_slide_data_init(SculptSession *ss, SculptBoundary *b continue; } copy_v3_v3(boundary->slide.directions[i], - boundary->slide.directions[boundary->edit_info[i].original_vertex]); + boundary->slide.directions[boundary->edit_info[i].original_vertex_i]); } } @@ -660,7 +695,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float t_orig_co[3]; float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); sub_v3_v3v3(t_orig_co, orig_data.co, boundary->bend.pivot_positions[vd.index]); @@ -671,7 +706,7 @@ static void do_boundary_brush_bend_task_cb_ex(void *__restrict userdata, add_v3_v3(target_co, boundary->bend.pivot_positions[vd.index]); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -708,7 +743,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -717,7 +752,7 @@ static void do_boundary_brush_slide_task_cb_ex(void *__restrict userdata, strength); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -754,7 +789,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -763,7 +798,7 @@ static void do_boundary_brush_inflate_task_cb_ex(void *__restrict userdata, strength); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -798,7 +833,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); madd_v3_v3v3fl(target_co, orig_data.co, @@ -806,7 +841,7 @@ static void do_boundary_brush_grab_task_cb_ex(void *__restrict userdata, boundary->edit_info[vd.index].strength_factor * mask * automask * strength); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -849,7 +884,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, } const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); float t_orig_co[3]; float *target_co = SCULPT_brush_deform_target_vertex_co_get(ss, brush->deform_target, &vd); sub_v3_v3v3(t_orig_co, orig_data.co, boundary->twist.pivot_position); @@ -860,7 +895,7 @@ static void do_boundary_brush_twist_task_cb_ex(void *__restrict userdata, add_v3_v3(target_co, boundary->twist.pivot_position); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -898,9 +933,9 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, int total_neighbors = 0; const int current_propagation_steps = boundary->edit_info[vd.index].num_propagation_steps; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { if (current_propagation_steps == boundary->edit_info[ni.index].num_propagation_steps) { - add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(coord_accum, SCULPT_vertex_co_get(ss, ni.vertex)); total_neighbors++; } } @@ -919,7 +954,7 @@ static void do_boundary_brush_smooth_task_cb_ex(void *__restrict userdata, target_co, vd.co, disp, boundary->edit_info[vd.index].strength_factor * mask * strength); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -933,7 +968,8 @@ void SCULPT_do_boundary_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totn const int symm_area = ss->cache->mirror_symmetry_pass; if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { - int initial_vertex; + PBVHVertRef initial_vertex; + if (ss->cache->mirror_symmetry_pass == 0) { initial_vertex = SCULPT_active_vertex_get(ss); } diff --git a/source/blender/editors/sculpt_paint/sculpt_brush_types.c b/source/blender/editors/sculpt_paint/sculpt_brush_types.c index c607dd02a77..245cbe0f54e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_brush_types.c +++ b/source/blender/editors/sculpt_paint/sculpt_brush_types.c @@ -314,13 +314,13 @@ static void do_draw_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -412,13 +412,13 @@ static void do_fill_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -510,13 +510,13 @@ static void do_scrape_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -628,13 +628,13 @@ static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -783,13 +783,13 @@ static void do_flatten_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } } @@ -940,13 +940,13 @@ static void do_clay_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1066,13 +1066,13 @@ static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1219,7 +1219,7 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); } @@ -1267,12 +1267,12 @@ static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, if (vd.mask) { mul_v3_fl(disp, 1.0f - *vd.mask); } - mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); + mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); copy_v3_v3(proxy[vd.i], disp); } if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1352,13 +1352,13 @@ static void do_thumb_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1426,7 +1426,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); sub_v3_v3v3(vec, orig_data.co, ss->cache->location); @@ -1436,7 +1436,7 @@ static void do_rotate_brush_task_cb_ex(void *__restrict userdata, sub_v3_v3(proxy[vd.i], orig_data.co); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1497,7 +1497,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); const int vi = vd.index; @@ -1533,9 +1533,10 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, float normal[3]; if (use_persistent_base) { - SCULPT_vertex_persistent_normal_get(ss, vi, normal); + SCULPT_vertex_persistent_normal_get(ss, vd.vertex, normal); mul_v3_fl(normal, brush->height); - madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor); + madd_v3_v3v3fl( + final_co, SCULPT_vertex_persistent_co_get(ss, vd.vertex), normal, *disp_factor); } else { copy_v3_v3(normal, orig_data.no); @@ -1551,7 +1552,7 @@ static void do_layer_brush_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, final_co); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1609,7 +1610,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float val[3]; @@ -1624,7 +1625,7 @@ static void do_inflate_brush_task_cb_ex(void *__restrict userdata, mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1677,13 +1678,13 @@ static void do_nudge_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], cono, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1756,7 +1757,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float val1[3]; float val2[3]; @@ -1777,7 +1778,7 @@ static void do_crease_brush_task_cb_ex(void *__restrict userdata, add_v3_v3v3(proxy[vd.i], val1, val2); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1872,7 +1873,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp_center[3]; float x_disp[3]; @@ -1896,7 +1897,7 @@ static void do_pinch_brush_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(proxy[vd.i], disp_center, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -1988,7 +1989,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (grab_silhouette) { @@ -2005,7 +2006,7 @@ static void do_grab_brush_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(proxy[vd.i], grab_delta, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2108,12 +2109,12 @@ static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata, mul_v3_fl(final_disp, 1.0f - *vd.mask); } - mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index)); + mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex)); copy_v3_v3(proxy[vd.i], final_disp); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2185,13 +2186,13 @@ static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], offset, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2268,7 +2269,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; float current_disp_norm[3]; @@ -2290,10 +2291,10 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vertex_disp[3]; float vertex_disp_norm[3]; - sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co); normalize_v3_v3(vertex_disp_norm, vertex_disp); if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) { madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp)); @@ -2304,7 +2305,7 @@ static void do_topology_slide_task_cb_ex(void *__restrict userdata, mul_v3_v3fl(proxy[vd.i], final_disp, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2323,31 +2324,31 @@ void SCULPT_relax_vertex(SculptSession *ss, int neighbor_count = 0; zero_v3(smooth_pos); zero_v3(boundary_normal); - const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index); + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->vertex); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { neighbor_count++; if (!filter_boundary_face_sets || - (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) { + (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.vertex))) { /* When the vertex to relax is boundary, use only connected boundary vertices for the average * position. */ if (is_boundary) { - if (!SCULPT_vertex_is_boundary(ss, ni.index)) { + if (!SCULPT_vertex_is_boundary(ss, ni.vertex)) { continue; } - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); avg_count++; /* Calculate a normal for the constraint plane using the edges of the boundary. */ float to_neighbor[3]; - sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co); + sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); normalize_v3(to_neighbor); add_v3_v3(boundary_normal, to_neighbor); } else { - add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.vertex)); avg_count++; } } @@ -2376,7 +2377,7 @@ void SCULPT_relax_vertex(SculptSession *ss, normalize_v3_v3(vno, boundary_normal); } else { - SCULPT_vertex_normal_get(ss, vd->index, vno); + SCULPT_vertex_normal_get(ss, vd->vertex, vno); } if (is_zero_v3(vno)) { @@ -2425,12 +2426,12 @@ static void do_topology_relax_task_cb_ex(void *__restrict userdata, orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2501,17 +2502,17 @@ static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float limit_co[3]; float disp[3]; - SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co); + SCULPT_vertex_limit_surface_get(ss, vd.vertex, limit_co); sub_v3_v3v3(disp, limit_co, vd.co); mul_v3_v3fl(proxy[vd.i], disp, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2567,7 +2568,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; @@ -2594,11 +2595,11 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, float weights_accum = 1.0f; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vertex_disp[3]; float vertex_disp_norm[3]; float neighbor_limit_co[3]; - SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co); + SCULPT_vertex_limit_surface_get(ss, ni.vertex, neighbor_limit_co); sub_v3_v3v3(vertex_disp, ss->cache->limit_surface_co[ni.index], ss->cache->limit_surface_co[vd.index]); @@ -2623,7 +2624,7 @@ static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata, interp_v3_v3v3(vd.co, vd.co, new_co, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2638,7 +2639,7 @@ static void do_displacement_smear_store_prev_disp_task_cb_ex( PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { sub_v3_v3v3(ss->cache->prev_displacement[vd.index], - SCULPT_vertex_co_get(ss, vd.index), + SCULPT_vertex_co_get(ss, vd.vertex), ss->cache->limit_surface_co[vd.index]); } BKE_pbvh_vertex_iter_end; @@ -2657,9 +2658,11 @@ void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes totvert, sizeof(float[3]), "prev displacement"); ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_limit_surface_get(ss, vertex, ss->cache->limit_surface_co[i]); sub_v3_v3v3(ss->cache->prev_displacement[i], - SCULPT_vertex_co_get(ss, i), + SCULPT_vertex_co_get(ss, vertex), ss->cache->limit_surface_co[i]); } } @@ -2722,7 +2725,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, const float fade = bstrength * SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) * + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.vertex, thread_id) * ss->cache->pressure; float avg[3], val[3]; @@ -2736,7 +2739,7 @@ static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, val); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -2799,7 +2802,7 @@ static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, } const float fade = SCULPT_brush_strength_factor( - ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id); + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.vertex, thread_id); if (bstrength > 0.0f) { (*vd.mask) += fade * bstrength * (1.0f - *vd.mask); diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index e29b2172ea7..b4b2c4e48c8 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -220,13 +220,16 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL; + PBVHVertRef vertex1 = BKE_pbvh_index_to_vertex(ss->pbvh, v1); + PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2); + if (use_persistent) { - length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), - SCULPT_vertex_persistent_co_get(ss, v2)); + length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, vertex1), + SCULPT_vertex_persistent_co_get(ss, vertex2)); } else { - length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, v1), - SCULPT_vertex_co_get(ss, v2)); + length_constraint->length = len_v3v3(SCULPT_vertex_co_get(ss, vertex1), + SCULPT_vertex_co_get(ss, vertex2)); } length_constraint->strength = 1.0f; @@ -370,7 +373,7 @@ static void do_cloth_brush_build_constraints_task_cb_ex( int tot_indices = 0; build_indices[tot_indices] = vd.index; tot_indices++; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { build_indices[tot_indices] = ni.index; tot_indices++; } @@ -540,7 +543,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float brush_disp[3]; @@ -784,7 +787,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( mul_v3_fl(pos_diff, (1.0f - cloth_sim->damping) * sim_factor); const float mask_v = (1.0f - (vd.mask ? *vd.mask : 0.0f)) * - SCULPT_automasking_factor_get(automasking, ss, vd.index); + SCULPT_automasking_factor_get(automasking, ss, vd.vertex); madd_v3_v3fl(cloth_sim->pos[i], pos_diff, mask_v); madd_v3_v3fl(cloth_sim->pos[i], cloth_sim->acceleration[i], mask_v); @@ -802,7 +805,7 @@ static void do_cloth_brush_solve_simulation_task_cb_ex( copy_v3_v3(vd.co, cloth_sim->pos[vd.index]); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -852,10 +855,13 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, mul_v3_v3fl(correction_vector_half, correction_vector, 0.5f); - const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, v1)) * - SCULPT_automasking_factor_get(automasking, ss, v1); - const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, v2)) * - SCULPT_automasking_factor_get(automasking, ss, v2); + PBVHVertRef vertex1 = BKE_pbvh_index_to_vertex(ss->pbvh, v1); + PBVHVertRef vertex2 = BKE_pbvh_index_to_vertex(ss->pbvh, v2); + + const float mask_v1 = (1.0f - SCULPT_vertex_mask_get(ss, vertex1)) * + SCULPT_automasking_factor_get(automasking, ss, vertex1); + const float mask_v2 = (1.0f - SCULPT_vertex_mask_get(ss, vertex2)) * + SCULPT_automasking_factor_get(automasking, ss, vertex2); float sim_location[3]; cloth_brush_simulation_location_get(ss, brush, sim_location); @@ -1129,15 +1135,17 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation const bool has_deformation_pos = cloth_sim->deformation_pos != NULL; const bool has_softbody_pos = cloth_sim->softbody_pos != NULL; for (int i = 0; i < totverts; i++) { - copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, i)); - copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(cloth_sim->last_iteration_pos[i], SCULPT_vertex_co_get(ss, vertex)); + copy_v3_v3(cloth_sim->init_pos[i], SCULPT_vertex_co_get(ss, vertex)); + copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, vertex)); if (has_deformation_pos) { - copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, vertex)); cloth_sim->deformation_strength[i] = 1.0f; } if (has_softbody_pos) { - copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, i)); + copy_v3_v3(cloth_sim->softbody_pos[i], SCULPT_vertex_co_get(ss, vertex)); } } } @@ -1146,7 +1154,9 @@ void SCULPT_cloth_brush_store_simulation_state(SculptSession *ss, SculptClothSim { const int totverts = SCULPT_vertex_count_get(ss); for (int i = 0; i < totverts; i++) { - copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex)); } } @@ -1427,13 +1437,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { float fade = vd.mask ? *vd.mask : 0.0f; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); fade = 1.0f - fade; float force[3] = {0.0f, 0.0f, 0.0f}; float disp[3], temp[3], transform[3][3]; if (ss->filter_cache->active_face_set != SCULPT_FACE_SET_NONE) { - if (!SCULPT_vertex_has_face_set(ss, vd.index, ss->filter_cache->active_face_set)) { + if (!SCULPT_vertex_has_face_set(ss, vd.vertex, ss->filter_cache->active_face_set)) { continue; } } @@ -1452,7 +1462,7 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, break; case CLOTH_FILTER_INFLATE: { float normal[3]; - SCULPT_vertex_normal_get(ss, vd.index, normal); + SCULPT_vertex_normal_get(ss, vd.vertex, normal); mul_v3_v3fl(force, normal, fade * data->filter_strength); } break; case CLOTH_FILTER_EXPAND: @@ -1517,7 +1527,9 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent const int totverts = SCULPT_vertex_count_get(ss); for (int i = 0; i < totverts; i++) { - copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(ss->filter_cache->cloth_sim->pos[i], SCULPT_vertex_co_get(ss, vertex)); } SculptThreadedTaskData data = { diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index 00503087e39..ebbb0fa429e 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -174,13 +174,13 @@ static void sample_detail_voxel(bContext *C, ViewContext *vc, const int mval[2]) BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); /* Average the edge length of the connected edges to the active vertex. */ - int active_vertex = SCULPT_active_vertex_get(ss); + PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); const float *active_vertex_co = SCULPT_active_vertex_co_get(ss); float edge_length = 0.0f; int tot = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { - edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.index)); + edge_length += len_v3v3(active_vertex_co, SCULPT_vertex_co_get(ss, ni.vertex)); tot += 1; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -578,14 +578,14 @@ static void dyntopo_detail_size_sample_from_surface(Object *ob, DyntopoDetailSizeEditCustomData *cd) { SculptSession *ss = ob->sculpt; - const int active_vertex = SCULPT_active_vertex_get(ss); + const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); float len_accum = 0; int num_neighbors = 0; SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, active_vertex, ni) { len_accum += len_v3v3(SCULPT_vertex_co_get(ss, active_vertex), - SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_vertex_co_get(ss, ni.vertex)); num_neighbors++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); diff --git a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c index 4f884420401..40b4b74a441 100644 --- a/source/blender/editors/sculpt_paint/sculpt_dyntopo.c +++ b/source/blender/editors/sculpt_paint/sculpt_dyntopo.c @@ -92,13 +92,16 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss) { int cd_node_layer_index; - char layer_id[] = "_dyntopo_node_id"; + char node_vertex_id[] = "_dyntopo_vnode_id"; + char node_face_id[] = "_dyntopo_fnode_id"; + + cd_node_layer_index = CustomData_get_named_layer_index( + &ss->bm->vdata, CD_PROP_INT32, node_vertex_id); - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT32, layer_id); if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, layer_id); + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT32, node_vertex_id); cd_node_layer_index = CustomData_get_named_layer_index( - &ss->bm->vdata, CD_PROP_INT32, layer_id); + &ss->bm->vdata, CD_PROP_INT32, node_vertex_id); } ss->cd_vert_node_offset = CustomData_get_n_offset( @@ -108,11 +111,12 @@ void SCULPT_dyntopo_node_layers_add(SculptSession *ss) ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT32, layer_id); + cd_node_layer_index = CustomData_get_named_layer_index( + &ss->bm->pdata, CD_PROP_INT32, node_face_id); if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, layer_id); + BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT32, node_face_id); cd_node_layer_index = CustomData_get_named_layer_index( - &ss->bm->pdata, CD_PROP_INT32, layer_id); + &ss->bm->pdata, CD_PROP_INT32, node_face_id); } ss->cd_face_node_offset = CustomData_get_n_offset( @@ -223,8 +227,9 @@ static void SCULPT_dynamic_topology_disable_ex( me->face_sets_color_default = 1; /* Sync the visibility to vertices manually as the pmap is still not initialized. */ - for (int i = 0; i < me->totvert; i++) { - me->mvert[i].flag &= ~ME_HIDE; + bool *hide_vert = (bool *)CustomData_get_layer_named(&me->vdata, CD_PROP_BOOL, ".hide_vert"); + if (hide_vert != NULL) { + memset(hide_vert, 0, sizeof(bool) * me->totvert); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index da2c346cf82..dd1c7a36c16 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -140,10 +140,12 @@ enum { */ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const PBVHVertRef v) { + int v_i = BKE_pbvh_vertex_to_index(ss->pbvh, v); + for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { - if (ss->vertex_info.connected_component[v] == expand_cache->active_connected_components[i]) { + if (ss->vertex_info.connected_component[v_i] == expand_cache->active_connected_components[i]) { return true; } } @@ -158,7 +160,7 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, const int f) { const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart]; - return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v); + return sculpt_expand_is_vert_in_active_component(ss, expand_cache, BKE_pbvh_make_vref(loop->v)); } /** @@ -167,14 +169,16 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, */ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const PBVHVertRef v) { + int v_i = BKE_pbvh_vertex_to_index(ss->pbvh, v); + if (expand_cache->texture_distortion_strength == 0.0f) { - return expand_cache->vert_falloff[v]; + return expand_cache->vert_falloff[v_i]; } if (!expand_cache->brush->mtex.tex) { - return expand_cache->vert_falloff[v]; + return expand_cache->vert_falloff[v_i]; } float rgba[4]; @@ -184,7 +188,7 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength * expand_cache->max_vert_falloff; - return expand_cache->vert_falloff[v] + distortion; + return expand_cache->vert_falloff[v_i] + distortion; } /** @@ -209,7 +213,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache) * Main function to get the state of a vertex for the current state and settings of a #ExpandCache. * Returns true when the target data should be modified by expand. */ -static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v) +static bool sculpt_expand_state_get(SculptSession *ss, + ExpandCache *expand_cache, + const PBVHVertRef v) { if (!SCULPT_vertex_visible_get(ss, v)) { return false; @@ -303,7 +309,7 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_ */ static float sculpt_expand_gradient_value_get(SculptSession *ss, ExpandCache *expand_cache, - const int v) + const PBVHVertRef v) { if (!expand_cache->falloff_gradient) { return 1.0f; @@ -347,7 +353,8 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa const int totvert = SCULPT_vertex_count_get(ss); BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); for (int i = 0; i < totvert; i++) { - const bool enabled = sculpt_expand_state_get(ss, expand_cache, i); + const bool enabled = sculpt_expand_state_get( + ss, expand_cache, BKE_pbvh_index_to_vertex(ss->pbvh, i)); BLI_BITMAP_SET(enabled_vertices, i, enabled); } return enabled_vertices; @@ -369,16 +376,18 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, continue; } + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + bool is_expand_boundary = false; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { if (!BLI_BITMAP_TEST(enabled_vertices, ni.index)) { is_expand_boundary = true; } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, i)) { + if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, vertex)) { is_expand_boundary = true; } @@ -394,12 +403,12 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, * Utility function to get the closet vertex after flipping an original vertex position based on * an symmetry pass iteration index. */ -static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, - const char symm_it, - const int original_vertex) +static PBVHVertRef sculpt_expand_get_vertex_index_for_symmetry_pass( + Object *ob, const char symm_it, const PBVHVertRef original_vertex) { SculptSession *ss = ob->sculpt; - int symm_vertex = SCULPT_EXPAND_VERTEX_NONE; + PBVHVertRef symm_vertex = {SCULPT_EXPAND_VERTEX_NONE}; + if (symm_it == 0) { symm_vertex = original_vertex; } @@ -415,7 +424,7 @@ static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, * Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking * symmetry into account. */ -static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v) +static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const PBVHVertRef v) { return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX); } @@ -432,20 +441,23 @@ typedef struct ExpandFloodFillData { } ExpandFloodFillData; static bool expand_topology_floodfill_cb( - SculptSession *UNUSED(ss), int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + ExpandFloodFillData *data = userdata; if (!is_duplicate) { - const float to_it = data->dists[from_v] + 1.0f; - data->dists[to_v] = to_it; + const float to_it = data->dists[from_v_i] + 1.0f; + data->dists[to_v_i] = to_it; } else { - data->dists[to_v] = data->dists[from_v]; + data->dists[to_v_i] = data->dists[from_v_i]; } return true; } -static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int v) +static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -470,23 +482,26 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons * This creates falloff patterns that follow and snap to the hard edges of the object. */ static bool mask_expand_normal_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + ExpandFloodFillData *data = userdata; if (!is_duplicate) { float current_normal[3], prev_normal[3]; SCULPT_vertex_normal_get(ss, to_v, current_normal); SCULPT_vertex_normal_get(ss, from_v, prev_normal); - const float from_edge_factor = data->edge_factor[from_v]; - data->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * from_edge_factor; - data->dists[to_v] = dot_v3v3(data->original_normal, current_normal) * - powf(from_edge_factor, data->edge_sensitivity); - CLAMP(data->dists[to_v], 0.0f, 1.0f); + const float from_edge_factor = data->edge_factor[from_v_i]; + data->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) * from_edge_factor; + data->dists[to_v_i] = dot_v3v3(data->original_normal, current_normal) * + powf(from_edge_factor, data->edge_sensitivity); + CLAMP(data->dists[to_v_i], 0.0f, 1.0f); } else { /* PBVH_GRIDS duplicate handling. */ - data->edge_factor[to_v] = data->edge_factor[from_v]; - data->dists[to_v] = data->dists[from_v]; + data->edge_factor[to_v_i] = data->edge_factor[from_v_i]; + data->dists[to_v_i] = data->dists[from_v_i]; } return true; @@ -494,7 +509,7 @@ static bool mask_expand_normal_floodfill_cb( static float *sculpt_expand_normal_falloff_create(Sculpt *sd, Object *ob, - const int v, + const PBVHVertRef v, const float edge_sensitivity) { SculptSession *ss = ob->sculpt; @@ -520,9 +535,11 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, for (int repeat = 0; repeat < 2; repeat++) { for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg = 0.0f; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { avg += dists[ni.index]; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -539,7 +556,7 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, * Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into * account. */ -static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) +static float *sculpt_expand_spherical_falloff_create(Object *ob, const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -554,11 +571,14 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); - if (symm_vertex != -1) { + const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); + if (symm_vertex.i != SCULPT_EXPAND_VERTEX_NONE) { const float *co = SCULPT_vertex_co_get(ss, symm_vertex); for (int i = 0; i < totvert; i++) { - dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i))); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, vertex))); } } } @@ -571,13 +591,13 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) * boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it * stays parallel to the boundary, increasing the falloff value by 1 on each step. */ -static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const int v) +static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "spherical dist"); BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); - GSQueue *queue = BLI_gsqueue_new(sizeof(int)); + GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef)); /* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */ const char symm = SCULPT_mesh_symmetry_xyz_get(ob); @@ -586,7 +606,8 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX); if (!boundary) { @@ -595,7 +616,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i for (int i = 0; i < boundary->num_vertices; i++) { BLI_gsqueue_push(queue, &boundary->vertices[i]); - BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]); + BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices_i[i]); } SCULPT_boundary_data_free(boundary); } @@ -607,17 +628,19 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i /* Propagate the values from the boundaries to the rest of the mesh. */ while (!BLI_gsqueue_is_empty(queue)) { - int v_next; + PBVHVertRef v_next; + BLI_gsqueue_pop(queue, &v_next); + int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_next, ni) { if (BLI_BITMAP_TEST(visited_vertices, ni.index)) { continue; } - dists[ni.index] = dists[v_next] + 1.0f; + dists[ni.index] = dists[v_next_i] + 1.0f; BLI_BITMAP_ENABLE(visited_vertices, ni.index); - BLI_gsqueue_push(queue, &ni.index); + BLI_gsqueue_push(queue, &ni.vertex); } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); } @@ -632,7 +655,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i * the base mesh faces when checking a vertex neighbor. For this reason, this is not implement * using the general flood-fill and sculpt neighbors accessors. */ -static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) +static float *sculpt_expand_diagonals_falloff_create(Object *ob, const PBVHVertRef v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); @@ -647,17 +670,19 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) /* Search and mask as visited the initial vertices using the enabled symmetry passes. */ BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); - GSQueue *queue = BLI_gsqueue_new(sizeof(int)); + GSQueue *queue = BLI_gsqueue_new(sizeof(PBVHVertRef)); const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char symm_it = 0; symm_it <= symm; symm_it++) { if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, v); + int symm_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, symm_vertex); BLI_gsqueue_push(queue, &symm_vertex); - BLI_BITMAP_ENABLE(visited_vertices, symm_vertex); + BLI_BITMAP_ENABLE(visited_vertices, symm_vertex_i); } if (BLI_gsqueue_is_empty(queue)) { @@ -667,17 +692,20 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) /* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */ Mesh *mesh = ob->data; while (!BLI_gsqueue_is_empty(queue)) { - int v_next; + PBVHVertRef v_next; BLI_gsqueue_pop(queue, &v_next); - for (int j = 0; j < ss->pmap[v_next].count; j++) { - MPoly *p = &ss->mpoly[ss->pmap[v_next].indices[j]]; + + int v_next_i = BKE_pbvh_vertex_to_index(ss->pbvh, v_next); + + for (int j = 0; j < ss->pmap[v_next_i].count; j++) { + MPoly *p = &ss->mpoly[ss->pmap[v_next_i].indices[j]]; for (int l = 0; l < p->totloop; l++) { - const int neighbor_v = mesh->mloop[p->loopstart + l].v; - if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) { + const PBVHVertRef neighbor_v = BKE_pbvh_make_vref(mesh->mloop[p->loopstart + l].v); + if (BLI_BITMAP_TEST(visited_vertices, neighbor_v.i)) { continue; } - dists[neighbor_v] = dists[v_next] + 1.0f; - BLI_BITMAP_ENABLE(visited_vertices, neighbor_v); + dists[neighbor_v.i] = dists[v_next_i] + 1.0f; + BLI_BITMAP_ENABLE(visited_vertices, neighbor_v.i); BLI_gsqueue_push(queue, &neighbor_v); } } @@ -701,11 +729,13 @@ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss, const int totvert = SCULPT_vertex_count_get(ss); expand_cache->max_vert_falloff = -FLT_MAX; for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (expand_cache->vert_falloff[i] == FLT_MAX) { continue; } - if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, vertex)) { continue; } @@ -856,7 +886,9 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob, if (!BLI_BITMAP_TEST(boundary_vertices, i)) { continue; } - SCULPT_floodfill_add_and_skip_initial(&flood, i); + + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + SCULPT_floodfill_add_and_skip_initial(&flood, vertex); } MEM_freeN(boundary_vertices); @@ -921,10 +953,12 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); for (int i = 0; i < totvert; i++) { - if (!SCULPT_vertex_has_unique_face_set(ss, i)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_unique_face_set(ss, vertex)) { continue; } - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { continue; } BLI_BITMAP_ENABLE(enabled_vertices, i); @@ -941,8 +975,10 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, if (internal_falloff) { for (int i = 0; i < totvert; i++) { - if (!(SCULPT_vertex_has_face_set(ss, i, active_face_set) && - SCULPT_vertex_has_unique_face_set(ss, i))) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!(SCULPT_vertex_has_face_set(ss, vertex, active_face_set) && + SCULPT_vertex_has_unique_face_set(ss, vertex))) { continue; } expand_cache->vert_falloff[i] *= -1.0f; @@ -960,7 +996,9 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, } else { for (int i = 0; i < totvert; i++) { - if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_has_face_set(ss, vertex, active_face_set)) { continue; } expand_cache->vert_falloff[i] = 0.0f; @@ -976,7 +1014,7 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( ExpandCache *expand_cache, Sculpt *sd, Object *ob, - const int v, + const PBVHVertRef v, eSculptExpandFalloffType falloff_type) { MEM_SAFE_FREE(expand_cache->vert_falloff); @@ -1128,7 +1166,7 @@ static void sculpt_expand_restore_color_data(SculptSession *ss, ExpandCache *exp PBVHNode *node = nodes[n]; PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_color_set(ss, vd.index, expand_cache->original_colors[vd.index]); + SCULPT_vertex_color_set(ss, vd.vertex, expand_cache->original_colors[vd.index]); } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_redraw(node); @@ -1215,12 +1253,12 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { const float initial_mask = *vd.mask; - const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); + const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex); float new_mask; if (enabled) { - new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index); + new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex); } else { new_mask = 0.0f; @@ -1284,13 +1322,13 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { float initial_color[4]; - SCULPT_vertex_color_get(ss, vd.index, initial_color); + SCULPT_vertex_color_get(ss, vd.vertex, initial_color); - const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); + const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.vertex); float fade; if (enabled) { - fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index); + fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.vertex); } else { fade = 0.0f; @@ -1311,7 +1349,7 @@ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, continue; } - SCULPT_vertex_color_set(ss, vd.index, final_color); + SCULPT_vertex_color_set(ss, vd.vertex, final_color); any_changed = true; } @@ -1358,14 +1396,18 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { expand_cache->original_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask"); for (int i = 0; i < totvert; i++) { - expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, i); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, vertex); } } if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) { expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_color_get(ss, i, expand_cache->original_colors[i]); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_color_get(ss, vertex, expand_cache->original_colors[i]); } } } @@ -1388,14 +1430,16 @@ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expa } } -static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int vertex) +static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const PBVHVertRef vertex) { SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; ExpandCache *expand_cache = ss->expand_cache; + int vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + /* Update the active factor in the cache. */ - if (vertex == SCULPT_EXPAND_VERTEX_NONE) { + if (vertex.i == SCULPT_EXPAND_VERTEX_NONE) { /* This means that the cursor is not over the mesh, so a valid active falloff can't be * determined. In this situations, don't evaluate enabled states and default all vertices in * connected components to enabled. */ @@ -1403,7 +1447,7 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v expand_cache->all_enabled = true; } else { - expand_cache->active_falloff = expand_cache->vert_falloff[vertex]; + expand_cache->active_falloff = expand_cache->vert_falloff[vertex_i]; expand_cache->all_enabled = false; } @@ -1444,14 +1488,16 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v * Updates the #SculptSession cursor data and gets the active vertex * if the cursor is over the mesh. */ -static int sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, const float mval[2]) +static PBVHVertRef sculpt_expand_target_vertex_update_and_get(bContext *C, + Object *ob, + const float mval[2]) { SculptSession *ss = ob->sculpt; SculptCursorGeometryInfo sgi; if (SCULPT_cursor_geometry_info_update(C, &sgi, mval, false)) { return SCULPT_active_vertex_get(ss); } - return SCULPT_EXPAND_VERTEX_NONE; + return BKE_pbvh_make_vref(SCULPT_EXPAND_VERTEX_NONE); } /** @@ -1487,15 +1533,17 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache const float *expand_init_co = SCULPT_vertex_co_get(ss, expand_cache->initial_active_vertex); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (!BLI_BITMAP_TEST(boundary_vertices, i)) { continue; } - if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) { + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, vertex)) { continue; } - const float *vertex_co = SCULPT_vertex_co_get(ss, i); + const float *vertex_co = SCULPT_vertex_co_get(ss, vertex); if (!SCULPT_check_vertex_pivot_symmetry(vertex_co, expand_init_co, symm)) { continue; @@ -1550,9 +1598,8 @@ static void sculpt_expand_finish(bContext *C) * Finds and stores in the #ExpandCache the sculpt connected component index for each symmetry pass * needed for expand. */ -static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, - ExpandCache *expand_cache, - const int initial_vertex) +static void sculpt_expand_find_active_connected_components_from_vert( + Object *ob, ExpandCache *expand_cache, const PBVHVertRef initial_vertex) { SculptSession *ss = ob->sculpt; for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { @@ -1565,11 +1612,13 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, continue; } - const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + const PBVHVertRef symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( ob, symm_it, initial_vertex); + int symm_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, symm_vertex); + expand_cache->active_connected_components[(int)symm_it] = - ss->vertex_info.connected_component[symm_vertex]; + ss->vertex_info.connected_component[symm_vertex_i]; } } @@ -1583,14 +1632,20 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C, const float mval[2]) { SculptSession *ss = ob->sculpt; - int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval); - if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) { + + PBVHVertRef initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval); + + if (initial_vertex.i == SCULPT_EXPAND_VERTEX_NONE) { /* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active * vertex in the sculpt session. */ initial_vertex = SCULPT_active_vertex_get(ss); } + + int initial_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, initial_vertex); + copy_v2_v2(ss->expand_cache->initial_mouse, mval); expand_cache->initial_active_vertex = initial_vertex; + expand_cache->initial_active_vertex_i = initial_vertex_i; expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss); if (expand_cache->next_face_set == SCULPT_FACE_SET_NONE) { @@ -1690,7 +1745,8 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event /* Update and get the active vertex (and face) from the cursor. */ const float mval_fl[2] = {UNPACK2(event->mval)}; - const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mval_fl); + const PBVHVertRef target_expand_vertex = sculpt_expand_target_vertex_update_and_get( + C, ob, mval_fl); /* Handle the modal keymap state changes. */ ExpandCache *expand_cache = ss->expand_cache; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 91763a15e37..f045ba841f3 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -142,7 +142,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) { @@ -161,11 +161,11 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); if (fade > 0.05f) { - SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set); + SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set); } } } @@ -199,7 +199,7 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, if (!sculpt_brush_test_sq_fn(&test, vd.co)) { continue; } - if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) { + if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) { continue; } @@ -210,12 +210,12 @@ static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -324,8 +324,11 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) if (mode == SCULPT_FACE_SET_MASKED) { for (int i = 0; i < tot_vert; i++) { - if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (SCULPT_vertex_mask_get(ss, vertex) >= threshold && + SCULPT_vertex_visible_get(ss, vertex)) { + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } } @@ -337,7 +340,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) * sets and the performance hit of rendering the overlay. */ bool all_visible = true; for (int i = 0; i < tot_vert; i++) { - if (!SCULPT_vertex_visible_get(ss, i)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { all_visible = false; break; } @@ -351,15 +356,19 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } for (int i = 0; i < tot_vert; i++) { - if (SCULPT_vertex_visible_get(ss, i)) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (SCULPT_vertex_visible_get(ss, vertex)) { + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } } if (mode == SCULPT_FACE_SET_ALL) { for (int i = 0; i < tot_vert; i++) { - SCULPT_vertex_face_set_set(ss, i, next_face_set); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_face_set_set(ss, vertex, next_face_set); } } @@ -869,7 +878,9 @@ static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op) * be synced from face sets to non-manifold vertices. */ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { for (int i = 0; i < tot_vert; i++) { - if (!SCULPT_vertex_visible_get(ss, i)) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (!SCULPT_vertex_visible_get(ss, vertex)) { hidden_vertex = true; break; } @@ -1222,9 +1233,11 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, SCULPT_boundary_info_ensure(ob); for (int i = 0; i < totvert; i++) { - fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, i) && - SCULPT_vertex_has_face_set(ss, i, active_face_set_id) && - SCULPT_vertex_has_unique_face_set(ss, i); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + fair_vertices[i] = !SCULPT_vertex_is_boundary(ss, vertex) && + SCULPT_vertex_has_face_set(ss, vertex, active_face_set_id) && + SCULPT_vertex_has_unique_face_set(ss, vertex); } MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 27092bf9a04..7a1e08ea713 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -104,7 +104,7 @@ static void color_filter_task_cb(void *__restrict userdata, float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); if (fade == 0.0f) { continue; } @@ -189,10 +189,10 @@ static void color_filter_task_cb(void *__restrict userdata, case COLOR_FILTER_SMOOTH: { fade = clamp_f(fade, -1.0f, 1.0f); float smooth_color[4]; - SCULPT_neighbor_color_average(ss, smooth_color, vd.index); + SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); if (fade < 0.0f) { interp_v4_v4v4(smooth_color, smooth_color, col, 0.5f); @@ -224,7 +224,7 @@ static void color_filter_task_cb(void *__restrict userdata, } } - SCULPT_vertex_color_set(ss, vd.index, final_color); + SCULPT_vertex_color_set(ss, vd.vertex, final_color); } BKE_pbvh_vertex_iter_end; BKE_pbvh_node_mark_update_color(data->nodes[n]); @@ -240,7 +240,8 @@ static void sculpt_color_presmooth_init(SculptSession *ss) } for (int i = 0; i < totvert; i++) { - SCULPT_vertex_color_get(ss, i, ss->filter_cache->pre_smoothed_color[i]); + SCULPT_vertex_color_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, i), ss->filter_cache->pre_smoothed_color[i]); } for (int iteration = 0; iteration < 2; iteration++) { @@ -249,7 +250,7 @@ static void sculpt_color_presmooth_init(SculptSession *ss) int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, BKE_pbvh_index_to_vertex(ss->pbvh, i), ni) { float col[4] = {0}; copy_v4_v4(col, ss->filter_cache->pre_smoothed_color[ni.index]); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c index a21656435a3..fa4fe191273 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mask.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mask.c @@ -103,7 +103,7 @@ static void mask_filter_task_cb(void *__restrict userdata, switch (mode) { case MASK_FILTER_SMOOTH: case MASK_FILTER_SHARPEN: { - float val = SCULPT_neighbor_mask_average(ss, vd.index); + float val = SCULPT_neighbor_mask_average(ss, vd.vertex); val -= *vd.mask; @@ -123,7 +123,7 @@ static void mask_filter_task_cb(void *__restrict userdata, } case MASK_FILTER_GROW: max = 0.0f; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; if (vmask_f > max) { max = vmask_f; @@ -134,7 +134,7 @@ static void mask_filter_task_cb(void *__restrict userdata, break; case MASK_FILTER_SHRINK: min = 1.0f; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; if (vmask_f < min) { min = vmask_f; @@ -214,7 +214,8 @@ static int sculpt_mask_filter_exec(bContext *C, wmOperator *op) if (ELEM(filter_type, MASK_FILTER_GROW, MASK_FILTER_SHRINK)) { prev_mask = MEM_mallocN(num_verts * sizeof(float), "prevmask"); for (int j = 0; j < num_verts; j++) { - prev_mask[j] = SCULPT_vertex_mask_get(ss, j); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, j); + prev_mask[j] = SCULPT_vertex_mask_get(ss, vertex); } } @@ -305,9 +306,9 @@ static float neighbor_dirty_mask(SculptSession *ss, PBVHVertexIter *vd) zero_v3(avg); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->vertex, ni) { float normalized[3]; - sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.index), vd->co); + sub_v3_v3v3(normalized, SCULPT_vertex_co_get(ss, ni.vertex), vd->co); normalize_v3(normalized); add_v3_v3(avg, normalized); total++; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 9e84d9ac65d..4f45b7917ec 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -296,7 +296,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); if (fade == 0.0f && filter_type != MESH_FILTER_SURFACE_SMOOTH) { /* Surface Smooth can't skip the loop for this vertex as it needs to calculate its @@ -314,7 +314,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, } if (filter_type == MESH_FILTER_RELAX_FACE_SETS) { - if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.index)) { + if (relax_face_sets == SCULPT_vertex_has_unique_face_set(ss, vd.vertex)) { continue; } } @@ -322,7 +322,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, switch (filter_type) { case MESH_FILTER_SMOOTH: fade = clamp_f(fade, -1.0f, 1.0f); - SCULPT_neighbor_coords_average_interior(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex); sub_v3_v3v3(val, avg, orig_co); madd_v3_v3v3fl(val, orig_co, val, fade); sub_v3_v3v3(disp, val, orig_co); @@ -385,7 +385,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, disp, vd.co, ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + vd.vertex, orig_data.co, ss->filter_cache->surface_smooth_shape_preservation); break; @@ -399,10 +399,10 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_sharpen[3] = {0.0f, 0.0f, 0.0f}; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float disp_n[3]; sub_v3_v3v3( - disp_n, SCULPT_vertex_co_get(ss, ni.index), SCULPT_vertex_co_get(ss, vd.index)); + disp_n, SCULPT_vertex_co_get(ss, ni.vertex), SCULPT_vertex_co_get(ss, vd.vertex)); mul_v3_fl(disp_n, ss->filter_cache->sharpen_factor[ni.index]); add_v3_v3(disp_sharpen, disp_n); } @@ -412,7 +412,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, float disp_avg[3]; float avg_co[3]; - SCULPT_neighbor_coords_average(ss, avg_co, vd.index); + SCULPT_neighbor_coords_average(ss, avg_co, vd.vertex); sub_v3_v3v3(disp_avg, avg_co, vd.co); mul_v3_v3fl( disp_avg, disp_avg, smooth_ratio * pow2f(ss->filter_cache->sharpen_factor[vd.index])); @@ -457,7 +457,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, } copy_v3_v3(vd.co, final_pos); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -473,9 +473,11 @@ static void mesh_filter_enhance_details_init_directions(SculptSession *ss) filter_cache->detail_directions = MEM_malloc_arrayN( totvert, sizeof(float[3]), "detail directions"); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SCULPT_neighbor_coords_average(ss, avg, vertex); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -500,7 +502,9 @@ static void mesh_filter_init_limit_surface_co(SculptSession *ss) filter_cache->limit_surface_co = MEM_malloc_arrayN( totvert, sizeof(float[3]), "limit surface co"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_limit_surface_get(ss, vertex, filter_cache->limit_surface_co[i]); } } @@ -520,9 +524,11 @@ static void mesh_filter_sharpen_init(SculptSession *ss, totvert, sizeof(float[3]), "sharpen detail direction"); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SCULPT_neighbor_coords_average(ss, avg, vertex); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } @@ -544,12 +550,14 @@ static void mesh_filter_sharpen_init(SculptSession *ss, smooth_iterations < filter_cache->sharpen_curvature_smooth_iterations; smooth_iterations++) { for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float direction_avg[3] = {0.0f, 0.0f, 0.0f}; float sharpen_avg = 0; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]); sharpen_avg += filter_cache->sharpen_factor[ni.index]; total++; @@ -576,7 +584,7 @@ static void mesh_filter_surface_smooth_displace_task_cb( float fade = vd.mask ? *vd.mask : 0.0f; fade = 1.0f - fade; fade *= data->filter_strength; - fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.index); + fade *= SCULPT_automasking_factor_get(ss->filter_cache->automasking, ss, vd.vertex); if (fade == 0.0f) { continue; } @@ -584,7 +592,7 @@ static void mesh_filter_surface_smooth_displace_task_cb( SCULPT_surface_smooth_displace_step(ss, vd.co, ss->filter_cache->surface_smooth_laplacian_disp, - vd.index, + vd.vertex, ss->filter_cache->surface_smooth_current_vertex, clamp_f(fade, 0.0f, 1.0f)); } diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c index 1beb5d48961..ecf8c4586ae 100644 --- a/source/blender/editors/sculpt_paint/sculpt_geodesic.c +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c @@ -279,9 +279,12 @@ static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices return dists; } - const float *first_affected_co = SCULPT_vertex_co_get(ss, first_affected); + const float *first_affected_co = SCULPT_vertex_co_get( + ss, BKE_pbvh_index_to_vertex(ss->pbvh, first_affected)); for (int i = 0; i < totvert; i++) { - dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, vertex)); } return dists; @@ -305,7 +308,7 @@ float *SCULPT_geodesic_distances_create(Object *ob, float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, Object *ob, - const int vertex, + const PBVHVertRef vertex, const float limit_radius) { SculptSession *ss = ob->sculpt; @@ -314,7 +317,8 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, const char symm = SCULPT_mesh_symmetry_xyz_get(ob); for (char i = 0; i <= symm; ++i) { if (SCULPT_is_symmetry_iteration_valid(i, symm)) { - int v = -1; + PBVHVertRef v = {PBVH_REF_NONE}; + if (i == 0) { v = vertex; } @@ -323,8 +327,8 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i); v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false); } - if (v != -1) { - BLI_gset_add(initial_vertices, POINTER_FROM_INT(v)); + if (v.i != PBVH_REF_NONE) { + BLI_gset_add(initial_vertices, POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ss->pbvh, v))); } } } @@ -334,10 +338,11 @@ float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, return dists; } -float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius) +float *SCULPT_geodesic_from_vertex(Object *ob, const PBVHVertRef vertex, const float limit_radius) { GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); - BLI_gset_add(initial_vertices, POINTER_FROM_INT(vertex)); + BLI_gset_add(initial_vertices, + POINTER_FROM_INT(BKE_pbvh_vertex_to_index(ob->sculpt->pbvh, vertex))); float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); BLI_gset_free(initial_vertices, NULL); return dists; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 166504ca8a9..6a10f7cad18 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -59,10 +59,13 @@ typedef struct SculptCursorGeometryInfo { typedef struct SculptVertexNeighborIter { /* Storage */ - int *neighbors; + PBVHVertRef *neighbors; + int *neighbor_indices; int size; int capacity; - int neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + + PBVHVertRef neighbors_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; + int neighbor_indices_fixed[SCULPT_VERTEX_NEIGHBOR_FIXED_CAPACITY]; /* Internal iterator. */ int num_duplicates; @@ -70,6 +73,7 @@ typedef struct SculptVertexNeighborIter { /* Public */ int index; + PBVHVertRef vertex; bool is_duplicate; } SculptVertexNeighborIter; @@ -318,7 +322,7 @@ typedef struct SculptThreadedTaskData { bool mask_by_color_preserve_mask; /* Index of the vertex that is going to be used as a reference for the colors. */ - int mask_by_color_vertex; + PBVHVertRef mask_by_color_vertex; float *mask_by_color_floodfill; int face_set; @@ -691,7 +695,8 @@ typedef struct ExpandCache { * during the execution of Expand by moving the origin. */ float initial_mouse_move[2]; float initial_mouse[2]; - int initial_active_vertex; + PBVHVertRef initial_active_vertex; + int initial_active_vertex_i; int initial_active_face_set; /* Maximum number of vertices allowed in the SculptSession for previewing the falloff using @@ -899,14 +904,14 @@ bool SCULPT_stroke_is_first_brush_step_of_symmetry_pass(struct StrokeCache *cach void SCULPT_vertex_random_access_ensure(struct SculptSession *ss); int SCULPT_vertex_count_get(struct SculptSession *ss); -const float *SCULPT_vertex_co_get(struct SculptSession *ss, int index); +const float *SCULPT_vertex_co_get(struct SculptSession *ss, PBVHVertRef vertex); /** Get the normal for a given sculpt vertex; do not modify the result */ -void SCULPT_vertex_normal_get(SculptSession *ss, int index, float no[3]); +void SCULPT_vertex_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); -float SCULPT_vertex_mask_get(struct SculptSession *ss, int index); -void SCULPT_vertex_color_get(const SculptSession *ss, int index, float r_color[4]); -void SCULPT_vertex_color_set(SculptSession *ss, int index, const float color[4]); +float SCULPT_vertex_mask_get(struct SculptSession *ss, PBVHVertRef vertex); +void SCULPT_vertex_color_get(const SculptSession *ss, PBVHVertRef vertex, float r_color[4]); +void SCULPT_vertex_color_set(SculptSession *ss, PBVHVertRef vertex, const float color[4]); /** Returns true if a color attribute exists in the current sculpt session. */ bool SCULPT_has_colors(const SculptSession *ss); @@ -914,19 +919,19 @@ bool SCULPT_has_colors(const SculptSession *ss); /** Returns true if the active color attribute is on loop (ATTR_DOMAIN_CORNER) domain. */ bool SCULPT_has_loop_colors(const struct Object *ob); -const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, int index); -void SCULPT_vertex_persistent_normal_get(SculptSession *ss, int index, float no[3]); +const float *SCULPT_vertex_persistent_co_get(SculptSession *ss, PBVHVertRef vertex); +void SCULPT_vertex_persistent_normal_get(SculptSession *ss, PBVHVertRef vertex, float no[3]); /** * Coordinates used for manipulating the base mesh when Grab Active Vertex is enabled. */ -const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, int index); +const float *SCULPT_vertex_co_for_grab_active_get(SculptSession *ss, PBVHVertRef vertex); /** * Returns the info of the limit surface when multi-res is available, * otherwise it returns the current coordinate of the vertex. */ -void SCULPT_vertex_limit_surface_get(SculptSession *ss, int index, float r_co[3]); +void SCULPT_vertex_limit_surface_get(SculptSession *ss, PBVHVertRef vertex, float r_co[3]); /** * Returns the pointer to the coordinates that should be edited from a brush tool iterator @@ -937,7 +942,7 @@ float *SCULPT_brush_deform_target_vertex_co_get(SculptSession *ss, PBVHVertexIter *iter); void SCULPT_vertex_neighbors_get(struct SculptSession *ss, - int index, + PBVHVertRef vertex, bool include_duplicates, SculptVertexNeighborIter *iter); @@ -946,7 +951,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, SCULPT_vertex_neighbors_get(ss, v_index, false, &neighbor_iterator); \ for (neighbor_iterator.i = 0; neighbor_iterator.i < neighbor_iterator.size; \ neighbor_iterator.i++) { \ - neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i]; + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; /** Iterate over neighboring and duplicate vertices (for PBVH_GRIDS). Duplicates come * first since they are nearest for floodfill. */ @@ -954,7 +960,8 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, SCULPT_vertex_neighbors_get(ss, v_index, true, &neighbor_iterator); \ for (neighbor_iterator.i = neighbor_iterator.size - 1; neighbor_iterator.i >= 0; \ neighbor_iterator.i--) { \ - neighbor_iterator.index = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.vertex = neighbor_iterator.neighbors[neighbor_iterator.i]; \ + neighbor_iterator.index = neighbor_iterator.neighbor_indices[neighbor_iterator.i]; \ neighbor_iterator.is_duplicate = (neighbor_iterator.i >= \ neighbor_iterator.size - neighbor_iterator.num_duplicates); @@ -965,7 +972,7 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss, } \ ((void)0) -int SCULPT_active_vertex_get(SculptSession *ss); +PBVHVertRef SCULPT_active_vertex_get(SculptSession *ss); const float *SCULPT_active_vertex_co_get(SculptSession *ss); void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3]); @@ -985,7 +992,7 @@ void SCULPT_fake_neighbors_free(struct Object *ob); /* Vertex Info. */ void SCULPT_boundary_info_ensure(Object *object); /* Boundary Info needs to be initialized in order to use this function. */ -bool SCULPT_vertex_is_boundary(const SculptSession *ss, int index); +bool SCULPT_vertex_is_boundary(const SculptSession *ss, PBVHVertRef vertex); void SCULPT_connected_components_ensure(Object *ob); @@ -995,8 +1002,8 @@ void SCULPT_connected_components_ensure(Object *ob); /** \name Sculpt Visibility API * \{ */ -void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible); -bool SCULPT_vertex_visible_get(SculptSession *ss, int index); +void SCULPT_vertex_visible_set(SculptSession *ss, PBVHVertRef vertex, bool visible); +bool SCULPT_vertex_visible_get(SculptSession *ss, PBVHVertRef vertex); void SCULPT_visibility_sync_all_face_sets_to_vertices(struct Object *ob); void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); @@ -1008,17 +1015,17 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss); * \{ */ int SCULPT_active_face_set_get(SculptSession *ss); -int SCULPT_vertex_face_set_get(SculptSession *ss, int index); -void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set); +int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex); +void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set); -bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set); -bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index); +bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set); +bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex); int SCULPT_face_set_next_available_get(SculptSession *ss); void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible); -bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index); -bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index); +bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, PBVHVertRef vertex); +bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, PBVHVertRef vertex); void SCULPT_face_sets_visibility_invert(SculptSession *ss); void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible); @@ -1104,11 +1111,11 @@ void SCULPT_calc_area_normal_and_center( void SCULPT_calc_area_center( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]); -int SCULPT_nearest_vertex_get(struct Sculpt *sd, - struct Object *ob, - const float co[3], - float max_distance, - bool use_original); +PBVHVertRef SCULPT_nearest_vertex_get(struct Sculpt *sd, + struct Object *ob, + const float co[3], + float max_distance, + bool use_original); int SCULPT_plane_point_side(const float co[3], const float plane[4]); int SCULPT_plane_trim(const struct StrokeCache *cache, @@ -1187,7 +1194,7 @@ float SCULPT_brush_strength_factor(struct SculptSession *ss, const float vno[3], const float fno[3], float mask, - int vertex_index, + const PBVHVertRef vertex, int thread_id); /** @@ -1218,15 +1225,18 @@ void SCULPT_floodfill_add_initial_with_symmetry(struct Sculpt *sd, struct Object *ob, struct SculptSession *ss, SculptFloodFill *flood, - int index, + PBVHVertRef vertex, float radius); -void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index); -void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index); -void SCULPT_floodfill_execute( - struct SculptSession *ss, - SculptFloodFill *flood, - bool (*func)(SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata), - void *userdata); +void SCULPT_floodfill_add_initial(SculptFloodFill *flood, PBVHVertRef vertex); +void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, PBVHVertRef vertex); +void SCULPT_floodfill_execute(struct SculptSession *ss, + SculptFloodFill *flood, + bool (*func)(SculptSession *ss, + PBVHVertRef from_v, + PBVHVertRef to_v, + bool is_duplicate, + void *userdata), + void *userdata); void SCULPT_floodfill_free(SculptFloodFill *flood); /** \} */ @@ -1276,7 +1286,7 @@ enum eDynTopoWarnFlag SCULPT_dynamic_topology_check(Scene *scene, Object *ob); float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking, SculptSession *ss, - int vert); + PBVHVertRef vertex); /* Returns the automasking cache depending on the active tool. Used for code that can run both for * brushes and filter. */ @@ -1309,9 +1319,9 @@ float *SCULPT_geodesic_distances_create(struct Object *ob, float limit_radius); float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd, struct Object *ob, - int vertex, + PBVHVertRef vertex, float limit_radius); -float *SCULPT_geodesic_from_vertex(Object *ob, int vertex, float limit_radius); +float *SCULPT_geodesic_from_vertex(Object *ob, PBVHVertRef vertex, float limit_radius); /** \} */ /* -------------------------------------------------------------------- */ @@ -1417,14 +1427,16 @@ BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush) */ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], struct BMVert *v); -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index); -float SCULPT_neighbor_mask_average(SculptSession *ss, int index); -void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index); +void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef vertex); +float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex); +void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef vertex); /** * Mask the mesh boundaries smoothing only the mesh surface without using auto-masking. */ -void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index); +void SCULPT_neighbor_coords_average_interior(SculptSession *ss, + float result[3], + PBVHVertRef vertex); void SCULPT_smooth( Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength, bool smooth_mask); @@ -1436,11 +1448,15 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], float (*laplacian_disp)[3], - int v_index, + PBVHVertRef vertex, const float origco[3], float alpha); -void SCULPT_surface_smooth_displace_step( - SculptSession *ss, float *co, float (*laplacian_disp)[3], int v_index, float beta, float fade); +void SCULPT_surface_smooth_displace_step(SculptSession *ss, + float *co, + float (*laplacian_disp)[3], + PBVHVertRef vertex, + float beta, + float fade); void SCULPT_do_surface_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode); /* Slide/Relax */ @@ -1646,7 +1662,7 @@ void SCULPT_pose_ik_chain_free(struct SculptPoseIKChain *ik_chain); */ struct SculptBoundary *SCULPT_boundary_data_init(Object *object, Brush *brush, - int initial_vertex, + PBVHVertRef initial_vertex, float radius); void SCULPT_boundary_data_free(struct SculptBoundary *boundary); /* Main Brush Function. */ diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 03243f00c49..2e661711172 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -97,11 +97,14 @@ static void sculpt_expand_task_cb(void *__restrict userdata, PBVHVertexIter vd; int update_it = data->mask_expand_update_it; + PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); + int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex); + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { int vi = vd.index; float final_mask = *vd.mask; if (data->mask_expand_use_normals) { - if (ss->filter_cache->normal_factor[SCULPT_active_vertex_get(ss)] < + if (ss->filter_cache->normal_factor[active_vertex_i] < ss->filter_cache->normal_factor[vd.index]) { final_mask = 1.0f; } @@ -121,7 +124,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata, if (data->mask_expand_create_face_set) { if (final_mask == 1.0f) { - SCULPT_vertex_face_set_set(ss, vd.index, ss->filter_cache->new_face_set); + SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set); } BKE_pbvh_node_mark_redraw(node); } @@ -164,10 +167,13 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * if (RNA_boolean_get(op->ptr, "use_cursor")) { SculptCursorGeometryInfo sgi; + const float mval_fl[2] = {UNPACK2(event->mval)}; if (SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) { + int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, SCULPT_active_vertex_get(ss)); + /* The cursor is over the mesh, get the update iteration from the updated active vertex. */ - mask_expand_update_it = ss->filter_cache->mask_update_it[(int)SCULPT_active_vertex_get(ss)]; + mask_expand_update_it = ss->filter_cache->mask_update_it[active_vertex_i]; } else { /* When the cursor is outside the mesh, affect the entire connected component. */ @@ -288,13 +294,16 @@ typedef struct MaskExpandFloodFillData { } MaskExpandFloodFillData; static bool mask_expand_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { MaskExpandFloodFillData *data = userdata; + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + if (!is_duplicate) { - int to_it = ss->filter_cache->mask_update_it[from_v] + 1; - ss->filter_cache->mask_update_it[to_v] = to_it; + int to_it = ss->filter_cache->mask_update_it[from_v_i] + 1; + ss->filter_cache->mask_update_it[to_v_i] = to_it; if (to_it > ss->filter_cache->mask_update_last_it) { ss->filter_cache->mask_update_last_it = to_it; } @@ -303,20 +312,20 @@ static bool mask_expand_floodfill_cb( float current_normal[3], prev_normal[3]; SCULPT_vertex_normal_get(ss, to_v, current_normal); SCULPT_vertex_normal_get(ss, from_v, prev_normal); - const float from_edge_factor = ss->filter_cache->edge_factor[from_v]; - ss->filter_cache->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * - from_edge_factor; - ss->filter_cache->normal_factor[to_v] = dot_v3v3(data->original_normal, current_normal) * - powf(from_edge_factor, data->edge_sensitivity); - CLAMP(ss->filter_cache->normal_factor[to_v], 0.0f, 1.0f); + const float from_edge_factor = ss->filter_cache->edge_factor[from_v_i]; + ss->filter_cache->edge_factor[to_v_i] = dot_v3v3(current_normal, prev_normal) * + from_edge_factor; + ss->filter_cache->normal_factor[to_v_i] = dot_v3v3(data->original_normal, current_normal) * + powf(from_edge_factor, data->edge_sensitivity); + CLAMP(ss->filter_cache->normal_factor[to_v_i], 0.0f, 1.0f); } } else { /* PBVH_GRIDS duplicate handling. */ - ss->filter_cache->mask_update_it[to_v] = ss->filter_cache->mask_update_it[from_v]; + ss->filter_cache->mask_update_it[to_v_i] = ss->filter_cache->mask_update_it[from_v_i]; if (data->use_normals) { - ss->filter_cache->edge_factor[to_v] = ss->filter_cache->edge_factor[from_v]; - ss->filter_cache->normal_factor[to_v] = ss->filter_cache->normal_factor[from_v]; + ss->filter_cache->edge_factor[to_v_i] = ss->filter_cache->edge_factor[from_v_i]; + ss->filter_cache->normal_factor[to_v_i] = ss->filter_cache->normal_factor[from_v_i]; } } @@ -389,13 +398,17 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent else { ss->filter_cache->prev_mask = MEM_callocN(sizeof(float) * vertex_count, "prev mask"); for (int i = 0; i < vertex_count; i++) { - ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, i); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + ss->filter_cache->prev_mask[i] = SCULPT_vertex_mask_get(ss, vertex); } } + int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, SCULPT_active_vertex_get(ss)); + ss->filter_cache->mask_update_last_it = 1; ss->filter_cache->mask_update_current_it = 1; - ss->filter_cache->mask_update_it[SCULPT_active_vertex_get(ss)] = 0; + ss->filter_cache->mask_update_it[active_vertex_i] = 0; copy_v3_v3(ss->filter_cache->mask_expand_initial_co, SCULPT_active_vertex_co_get(ss)); @@ -414,9 +427,11 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent if (use_normals) { for (int repeat = 0; repeat < 2; repeat++) { for (int i = 0; i < vertex_count; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg = 0.0f; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { avg += ss->filter_cache->normal_factor[ni.index]; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_init.c b/source/blender/editors/sculpt_paint/sculpt_mask_init.c index 025f34ab2d7..cc27623adb0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_init.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_init.c @@ -99,7 +99,7 @@ static void mask_init_task_cb(void *__restrict userdata, *vd.mask = BLI_hash_int_01(vd.index + seed); break; case SCULPT_MASK_INIT_RANDOM_PER_FACE_SET: { - const int face_set = SCULPT_vertex_face_set_get(ss, vd.index); + const int face_set = SCULPT_vertex_face_set_get(ss, vd.vertex); *vd.mask = BLI_hash_int_01(face_set + seed); break; } diff --git a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c index 50cb75e8089..1e8731e54c0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c +++ b/source/blender/editors/sculpt_paint/sculpt_multiplane_scrape.c @@ -86,7 +86,7 @@ static void calc_multiplane_scrape_surface_task_cb(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); /* Sample the normal and area of the +X and -X axis individually. */ @@ -194,13 +194,13 @@ static void do_multiplane_scrape_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); mul_v3_v3fl(proxy[vd.i], val, fade); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c index 8f96f5cddea..151eb7744ea 100644 --- a/source/blender/editors/sculpt_paint/sculpt_ops.c +++ b/source/blender/editors/sculpt_paint/sculpt_ops.c @@ -129,8 +129,10 @@ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) "layer persistent base"); for (int i = 0; i < totvert; i++) { - copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i)); - SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, vertex)); + SCULPT_vertex_normal_get(ss, vertex, ss->persistent_base[i].no); ss->persistent_base[i].disp = 0.0f; } @@ -543,7 +545,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Object *ob = CTX_data_active_object(C); - ss->preview_vert_index_count = 0; + ss->preview_vert_count = 0; int totpoints = 0; /* This function is called from the cursor drawing code, so the PBVH may not be build yet. */ @@ -573,29 +575,32 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float /* Assuming an average of 6 edges per vertex in a triangulated mesh. */ const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2; - if (ss->preview_vert_index_list == NULL) { - ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines"); + if (ss->preview_vert_list == NULL) { + ss->preview_vert_list = MEM_callocN(max_preview_vertices * sizeof(PBVHVertRef), + "preview lines"); } - GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int)); - int active_v = SCULPT_active_vertex_get(ss); + GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(PBVHVertRef)); + PBVHVertRef active_v = SCULPT_active_vertex_get(ss); BLI_gsqueue_push(not_visited_vertices, &active_v); while (!BLI_gsqueue_is_empty(not_visited_vertices)) { - int from_v; + PBVHVertRef from_v; + BLI_gsqueue_pop(not_visited_vertices, &from_v); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) { if (totpoints + (ni.size * 2) < max_preview_vertices) { - int to_v = ni.index; - ss->preview_vert_index_list[totpoints] = from_v; + PBVHVertRef to_v = ni.vertex; + int to_v_i = ni.index; + ss->preview_vert_list[totpoints] = from_v; totpoints++; - ss->preview_vert_index_list[totpoints] = to_v; + ss->preview_vert_list[totpoints] = to_v; totpoints++; - if (BLI_BITMAP_TEST(visited_vertices, to_v)) { + if (BLI_BITMAP_TEST(visited_vertices, to_v_i)) { continue; } - BLI_BITMAP_ENABLE(visited_vertices, to_v); + BLI_BITMAP_ENABLE(visited_vertices, to_v_i); const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v); if (len_squared_v3v3(brush_co, co) < radius * radius) { BLI_gsqueue_push(not_visited_vertices, &to_v); @@ -609,152 +614,7 @@ void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float MEM_freeN(visited_vertices); - ss->preview_vert_index_count = totpoints; -} - -static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - - ID *data; - data = ob->data; - if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) { - return OPERATOR_CANCELLED; - } - - if (ob->type != OB_MESH) { - return OPERATOR_CANCELLED; - } - - Mesh *mesh = ob->data; - - const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR); - if (mloopcol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); - - const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); - if (MPropCol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - const MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - - const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); - - for (int i = 0; i < mesh->totpoly; i++) { - const MPoly *c_poly = &polys[i]; - for (int j = 0; j < c_poly->totloop; j++) { - int loop_index = c_poly->loopstart + j; - const MLoop *c_loop = &loops[c_poly->loopstart + j]; - float srgb_color[4]; - linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); - loopcols[loop_index].r = (char)(srgb_color[0] * 255); - loopcols[loop_index].g = (char)(srgb_color[1] * 255); - loopcols[loop_index].b = (char)(srgb_color[2] * 255); - loopcols[loop_index].a = (char)(srgb_color[3] * 255); - } - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - return OPERATOR_FINISHED; -} - -static bool sculpt_colors_poll(bContext *C) -{ - if (!SCULPT_mode_poll(C)) { - return false; - } - - Object *ob = CTX_data_active_object(C); - - if (!ob->sculpt || !ob->sculpt->pbvh || BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_FACES) { - return false; - } - - return SCULPT_has_colors(ob->sculpt); -} - -static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Sculpt Vertex Color to Vertex Color"; - ot->description = "Copy the Sculpt Vertex Color to a regular color layer"; - ot->idname = "SCULPT_OT_vertex_to_loop_colors"; - - /* api callbacks */ - ot->poll = sculpt_colors_poll; - ot->exec = vertex_to_loop_colors_exec; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; -} - -static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Object *ob = CTX_data_active_object(C); - - ID *data; - data = ob->data; - if (data == NULL || ID_IS_LINKED(data) || ID_IS_OVERRIDE_LIBRARY(data)) { - return OPERATOR_CANCELLED; - } - - if (ob->type != OB_MESH) { - return OPERATOR_CANCELLED; - } - - Mesh *mesh = ob->data; - - const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_PROP_BYTE_COLOR); - if (mloopcol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - const MLoopCol *loopcols = CustomData_get_layer_n( - &mesh->ldata, CD_PROP_BYTE_COLOR, mloopcol_layer_n); - - const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR); - if (MPropCol_layer_n == -1) { - return OPERATOR_CANCELLED; - } - MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n); - - const MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP); - const MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY); - - for (int i = 0; i < mesh->totpoly; i++) { - const MPoly *c_poly = &polys[i]; - for (int j = 0; j < c_poly->totloop; j++) { - int loop_index = c_poly->loopstart + j; - const MLoop *c_loop = &loops[c_poly->loopstart + j]; - vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f); - vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); - vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); - vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f); - srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color); - } - } - - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); - - return OPERATOR_FINISHED; -} - -static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Vertex Color to Sculpt Vertex Color"; - ot->description = "Copy the active loop color layer to the vertex color"; - ot->idname = "SCULPT_OT_loop_to_vertex_colors"; - - /* api callbacks */ - ot->poll = sculpt_colors_poll; - ot->exec = loop_to_vertex_colors_exec; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ss->preview_vert_count = totpoints; } static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) @@ -764,7 +624,7 @@ static int sculpt_sample_color_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = CTX_data_active_object(C); Brush *brush = BKE_paint_brush(&sd->paint); SculptSession *ss = ob->sculpt; - int active_vertex = SCULPT_active_vertex_get(ss); + PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); float active_vertex_color[4]; if (!SCULPT_handles_colors_report(ss, op->reports)) { @@ -890,8 +750,11 @@ static void do_mask_by_color_contiguous_update_nodes_cb( } static bool sculpt_mask_by_color_contiguous_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + MaskByColorContiguousFloodFillData *data = userdata; float current_color[4]; @@ -899,10 +762,10 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb( float new_vertex_mask = sculpt_mask_by_color_delta_get( current_color, data->initial_color, data->threshold, data->invert); - data->new_mask[to_v] = new_vertex_mask; + data->new_mask[to_v_i] = new_vertex_mask; if (is_duplicate) { - data->new_mask[to_v] = data->new_mask[from_v]; + data->new_mask[to_v_i] = data->new_mask[from_v_i]; } float len = len_v3v3(current_color, data->initial_color); @@ -911,7 +774,7 @@ static bool sculpt_mask_by_color_contiguous_floodfill_cb( } static void sculpt_mask_by_color_contiguous(Object *object, - const int vertex, + const PBVHVertRef vertex, const float threshold, const bool invert, const bool preserve_mask) @@ -988,7 +851,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); const float current_mask = *vd.mask; const float new_mask = sculpt_mask_by_color_delta_get(active_color, col, threshold, invert); @@ -1006,7 +869,7 @@ static void do_mask_by_color_task_cb(void *__restrict userdata, } static void sculpt_mask_by_color_full_mesh(Object *object, - const int vertex, + const PBVHVertRef vertex, const float threshold, const bool invert, const bool preserve_mask) @@ -1061,7 +924,7 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven SCULPT_undo_push_begin(ob, "Mask by color"); BKE_sculpt_color_layer_create_if_needed(ob); - const int active_vertex = SCULPT_active_vertex_get(ss); + const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); const float threshold = RNA_float_get(op->ptr, "threshold"); const bool invert = RNA_boolean_get(op->ptr, "invert"); const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask"); @@ -1150,8 +1013,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_project_line_gesture); WM_operatortype_append(SCULPT_OT_sample_color); - WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors); - WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors); WM_operatortype_append(SCULPT_OT_color_filter); WM_operatortype_append(SCULPT_OT_mask_by_color); WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit); diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c index 3b65124fe02..c494c71f1eb 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c @@ -81,16 +81,16 @@ static void do_color_smooth_task_cb_exec(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float smooth_color[4]; - SCULPT_neighbor_color_average(ss, smooth_color, vd.index); + SCULPT_neighbor_color_average(ss, smooth_color, vd.vertex); float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); blend_color_interpolate_float(col, col, smooth_color, fade); - SCULPT_vertex_color_set(ss, vd.index, col); + SCULPT_vertex_color_set(ss, vd.vertex, col); } BKE_pbvh_vertex_iter_end; } @@ -168,7 +168,7 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); /* Density. */ @@ -199,10 +199,10 @@ static void do_paint_brush_task_cb_ex(void *__restrict userdata, mul_v4_v4fl(buffer_color, color_buffer->color[vd.i], brush->alpha); float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); IMB_blend_color_float(col, orig_data.col, buffer_color, brush->blend); CLAMP4(col, 0.0f, 1.0f); - SCULPT_vertex_color_set(ss, vd.index, col); + SCULPT_vertex_color_set(ss, vd.vertex, col); } BKE_pbvh_vertex_iter_end; } @@ -234,7 +234,7 @@ static void do_sample_wet_paint_task_cb(void *__restrict userdata, } float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); add_v4_v4(swptd->color, col); swptd->tot_samples++; @@ -413,7 +413,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float current_disp[3]; @@ -422,7 +422,7 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]); float no[3]; - SCULPT_vertex_normal_get(ss, vd.index, no); + SCULPT_vertex_normal_get(ss, vd.vertex, no); switch (brush->smear_deform_type) { case BRUSH_SMEAR_DEFORM_DRAG: @@ -455,11 +455,11 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, */ SculptVertexNeighborIter ni2; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni2) { - const float *nco = SCULPT_vertex_co_get(ss, ni2.index); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni2) { + const float *nco = SCULPT_vertex_co_get(ss, ni2.vertex); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, ni2.vertex, ni) { if (ni.index == vd.index) { continue; } @@ -467,13 +467,13 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, float vertex_disp[3]; float vertex_disp_norm[3]; - sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co); + sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.vertex), vd.co); /* Weight by how close we are to our target distance from vd.co. */ float w = (1.0f + fabsf(len_v3(vertex_disp) / bstrength - 1.0f)); /* TODO: use cotangents (or at least face areas) here. */ - float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.index), nco); + float len = len_v3v3(SCULPT_vertex_co_get(ss, ni.vertex), nco); if (len > 0.0f) { len = bstrength / len; } @@ -515,9 +515,9 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata, blend_color_mix_float(interp_color, interp_color, accum); float col[4]; - SCULPT_vertex_color_get(ss, vd.index, col); + SCULPT_vertex_color_get(ss, vd.vertex, col); blend_color_interpolate_float(col, ss->cache->prev_colors[vd.index], interp_color, fade); - SCULPT_vertex_color_set(ss, vd.index, col); + SCULPT_vertex_color_set(ss, vd.vertex, col); } BKE_pbvh_vertex_iter_end; } @@ -531,7 +531,7 @@ static void do_smear_store_prev_colors_task_cb_exec(void *__restrict userdata, PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_color_get(ss, vd.index, ss->cache->prev_colors[vd.index]); + SCULPT_vertex_color_get(ss, vd.vertex, ss->cache->prev_colors[vd.index]); } BKE_pbvh_vertex_iter_end; } @@ -550,7 +550,9 @@ void SCULPT_do_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode if (!ss->cache->prev_colors) { ss->cache->prev_colors = MEM_callocN(sizeof(float[4]) * totvert, "prev colors"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_color_get(ss, i, ss->cache->prev_colors[i]); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + SCULPT_vertex_color_get(ss, vertex, ss->cache->prev_colors[i]); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc index f51a603ee5d..8a3a3fe7adc 100644 --- a/source/blender/editors/sculpt_paint/sculpt_paint_image.cc +++ b/source/blender/editors/sculpt_paint/sculpt_paint_image.cc @@ -172,7 +172,15 @@ template<typename ImageBuffer> class PaintingKernel { const float3 face_normal(0.0f, 0.0f, 0.0f); const float mask = 0.0f; const float falloff_strength = SCULPT_brush_strength_factor( - ss, brush, pixel_pos, sqrtf(test.dist), normal, face_normal, mask, 0, thread_id); + ss, + brush, + pixel_pos, + sqrtf(test.dist), + normal, + face_normal, + mask, + BKE_pbvh_make_vref(PBVH_REF_NONE), + thread_id); float4 paint_color = brush_color * falloff_strength * brush_strength; float4 buffer_color; blend_color_mix_float(buffer_color, color, paint_color); diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index 6f600489729..d1418c8dc35 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -182,7 +182,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, /* Apply the vertex mask to the displacement. */ const float mask = vd.mask ? 1.0f - *vd.mask : 1.0f; - const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index); + const float automask = SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.vertex); mul_v3_fl(disp, mask * automask); /* Accumulate the displacement. */ @@ -196,7 +196,7 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata, copy_v3_v3(target_co, final_pos); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -221,7 +221,7 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, float max = 0.0f; /* Grow the factor. */ - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { float vmask_f = data->prev_mask[ni.index]; max = MAX2(vmask_f, max); } @@ -367,7 +367,7 @@ typedef struct PoseFloodFillData { int current_face_set; int next_face_set; int prev_face_set; - int next_vertex; + PBVHVertRef next_vertex; bool next_face_set_found; @@ -397,14 +397,19 @@ typedef struct PoseFloodFillData { int target_face_set; } PoseFloodFillData; -static bool pose_topology_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) +static bool pose_topology_floodfill_cb(SculptSession *ss, + PBVHVertRef UNUSED(from_v), + PBVHVertRef to_v, + bool is_duplicate, + void *userdata) { + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + PoseFloodFillData *data = userdata; const float *co = SCULPT_vertex_co_get(ss, to_v); if (data->pose_factor) { - data->pose_factor[to_v] = 1.0f; + data->pose_factor[to_v_i] = 1.0f; } if (len_squared_v3v3(data->pose_initial_co, data->fallback_floodfill_origin) < @@ -426,15 +431,19 @@ static bool pose_topology_floodfill_cb( return false; } -static bool pose_face_sets_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool is_duplicate, void *userdata) +static bool pose_face_sets_floodfill_cb(SculptSession *ss, + PBVHVertRef UNUSED(from_v), + PBVHVertRef to_v, + bool is_duplicate, + void *userdata) { PoseFloodFillData *data = userdata; - const int index = to_v; + const int index = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + const PBVHVertRef vertex = to_v; bool visit_next = false; - const float *co = SCULPT_vertex_co_get(ss, index); + const float *co = SCULPT_vertex_co_get(ss, vertex); const bool symmetry_check = SCULPT_check_vertex_pivot_symmetry( co, data->pose_initial_co, data->symm) && !is_duplicate; @@ -448,11 +457,11 @@ static bool pose_face_sets_floodfill_cb( if (sculpt_pose_brush_is_vertex_inside_brush_radius( co, data->pose_initial_co, data->radius, data->symm)) { - const int visited_face_set = SCULPT_vertex_face_set_get(ss, index); + const int visited_face_set = SCULPT_vertex_face_set_get(ss, vertex); BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(visited_face_set)); } else if (symmetry_check) { - data->current_face_set = SCULPT_vertex_face_set_get(ss, index); + data->current_face_set = SCULPT_vertex_face_set_get(ss, vertex); BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(data->current_face_set)); } return true; @@ -466,11 +475,11 @@ static bool pose_face_sets_floodfill_cb( GSetIterator gs_iter; GSET_ITER (gs_iter, data->visited_face_sets) { const int visited_face_set = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); - is_vertex_valid |= SCULPT_vertex_has_face_set(ss, index, visited_face_set); + is_vertex_valid |= SCULPT_vertex_has_face_set(ss, vertex, visited_face_set); } } else { - is_vertex_valid = SCULPT_vertex_has_face_set(ss, index, data->current_face_set); + is_vertex_valid = SCULPT_vertex_has_face_set(ss, vertex, data->current_face_set); } if (!is_vertex_valid) { @@ -485,11 +494,11 @@ static bool pose_face_sets_floodfill_cb( /* Fallback origin accumulation. */ if (symmetry_check) { - add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, index)); + add_v3_v3(data->fallback_origin, SCULPT_vertex_co_get(ss, vertex)); data->fallback_count++; } - if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, index)) { + if (!symmetry_check || SCULPT_vertex_has_unique_face_set(ss, vertex)) { return visit_next; } @@ -498,15 +507,15 @@ static bool pose_face_sets_floodfill_cb( bool count_as_boundary = false; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.index); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + int next_face_set_candidate = SCULPT_vertex_face_set_get(ss, ni.vertex); /* Check if we can get a valid face set for the next iteration from this neighbor. */ - if (SCULPT_vertex_has_unique_face_set(ss, ni.index) && + if (SCULPT_vertex_has_unique_face_set(ss, ni.vertex) && !BLI_gset_haskey(data->visited_face_sets, POINTER_FROM_INT(next_face_set_candidate))) { if (!data->next_face_set_found) { data->next_face_set = next_face_set_candidate; - data->next_vertex = ni.index; + data->next_vertex = ni.vertex; data->next_face_set_found = true; } count_as_boundary = true; @@ -516,7 +525,7 @@ static bool pose_face_sets_floodfill_cb( /* Origin accumulation. */ if (count_as_boundary) { - add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, index)); + add_v3_v3(data->pose_origin, SCULPT_vertex_co_get(ss, vertex)); data->tot_co++; } return visit_next; @@ -585,7 +594,7 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata, SculptVertexNeighborIter ni; float avg = 0.0f; int total = 0; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.vertex, ni) { avg += data->pose_factor[ni.index]; total++; } @@ -660,7 +669,8 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd, float next_chain_segment_target[3]; int totvert = SCULPT_vertex_count_get(ss); - int nearest_vertex_index = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true); + PBVHVertRef nearest_vertex = SCULPT_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true); + int nearest_vertex_index = BKE_pbvh_vertex_to_index(ss->pbvh, nearest_vertex); /* Init the buffers used to keep track of the changes in the pose factors as more segments are * added to the IK chain. */ @@ -745,7 +755,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( int current_face_set = SCULPT_FACE_SET_NONE; int prev_face_set = SCULPT_FACE_SET_NONE; - int current_vertex = SCULPT_active_vertex_get(ss); + PBVHVertRef current_vertex = SCULPT_active_vertex_get(ss); for (int s = 0; s < ik_chain->tot_segments; s++) { @@ -801,15 +811,18 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets( } static bool pose_face_sets_fk_find_masked_floodfill_cb( - SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) + SculptSession *ss, PBVHVertRef from_v, PBVHVertRef to_v, bool is_duplicate, void *userdata) { PoseFloodFillData *data = userdata; + int from_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, from_v); + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + if (!is_duplicate) { - data->floodfill_it[to_v] = data->floodfill_it[from_v] + 1; + data->floodfill_it[to_v_i] = data->floodfill_it[from_v_i] + 1; } else { - data->floodfill_it[to_v] = data->floodfill_it[from_v]; + data->floodfill_it[to_v_i] = data->floodfill_it[from_v_i]; } const int to_face_set = SCULPT_vertex_face_set_get(ss, to_v); @@ -820,9 +833,9 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb( BLI_gset_add(data->visited_face_sets, POINTER_FROM_INT(to_face_set)); - if (data->floodfill_it[to_v] >= data->masked_face_set_it) { + if (data->floodfill_it[to_v_i] >= data->masked_face_set_it) { data->masked_face_set = to_face_set; - data->masked_face_set_it = data->floodfill_it[to_v]; + data->masked_face_set_it = data->floodfill_it[to_v_i]; } if (data->target_face_set == SCULPT_FACE_SET_NONE) { @@ -834,11 +847,17 @@ static bool pose_face_sets_fk_find_masked_floodfill_cb( return SCULPT_vertex_has_face_set(ss, to_v, data->initial_face_set); } -static bool pose_face_sets_fk_set_weights_floodfill_cb( - SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata) +static bool pose_face_sets_fk_set_weights_floodfill_cb(SculptSession *ss, + PBVHVertRef UNUSED(from_v), + PBVHVertRef to_v, + bool UNUSED(is_duplicate), + void *userdata) { PoseFloodFillData *data = userdata; - data->fk_weights[to_v] = 1.0f; + + int to_v_i = BKE_pbvh_vertex_to_index(ss->pbvh, to_v); + + data->fk_weights[to_v_i] = 1.0f; return !SCULPT_vertex_has_face_set(ss, to_v, data->masked_face_set); } @@ -849,7 +868,9 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( SculptPoseIKChain *ik_chain = pose_ik_chain_new(1, totvert); - const int active_vertex = SCULPT_active_vertex_get(ss); + const PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); + int active_vertex_index = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex); + const int active_face_set = SCULPT_active_face_set_get(ss); SculptFloodFill flood; @@ -857,7 +878,7 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( SCULPT_floodfill_add_initial(&flood, active_vertex); PoseFloodFillData fdata; fdata.floodfill_it = MEM_calloc_arrayN(totvert, sizeof(int), "floodfill iteration"); - fdata.floodfill_it[active_vertex] = 1; + fdata.floodfill_it[active_vertex_index] = 1; fdata.initial_face_set = active_face_set; fdata.masked_face_set = SCULPT_FACE_SET_NONE; fdata.target_face_set = SCULPT_FACE_SET_NONE; @@ -870,9 +891,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( int origin_count = 0; float origin_acc[3] = {0.0f}; for (int i = 0; i < totvert; i++) { - if (fdata.floodfill_it[i] != 0 && SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) && - SCULPT_vertex_has_face_set(ss, i, fdata.masked_face_set)) { - add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, i)); + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + + if (fdata.floodfill_it[i] != 0 && + SCULPT_vertex_has_face_set(ss, vertex, fdata.initial_face_set) && + SCULPT_vertex_has_face_set(ss, vertex, fdata.masked_face_set)) { + add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, vertex)); origin_count++; } } @@ -881,10 +905,12 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk( float target_acc[3] = {0.0f}; if (fdata.target_face_set != fdata.masked_face_set) { for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + if (fdata.floodfill_it[i] != 0 && - SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) && - SCULPT_vertex_has_face_set(ss, i, fdata.target_face_set)) { - add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, i)); + SCULPT_vertex_has_face_set(ss, vertex, fdata.initial_face_set) && + SCULPT_vertex_has_face_set(ss, vertex, fdata.target_face_set)) { + add_v3_v3(target_acc, SCULPT_vertex_co_get(ss, vertex)); target_count++; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index d6b9b500501..2ef3c28ba0c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -46,26 +46,28 @@ #include <math.h> #include <stdlib.h> -void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], int index) +void SCULPT_neighbor_coords_average_interior(SculptSession *ss, + float result[3], + PBVHVertRef vertex) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; int neighbor_count = 0; - const bool is_boundary = SCULPT_vertex_is_boundary(ss, index); + const bool is_boundary = SCULPT_vertex_is_boundary(ss, vertex); SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { neighbor_count++; if (is_boundary) { /* Boundary vertices use only other boundary vertices. */ - if (SCULPT_vertex_is_boundary(ss, ni.index)) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + if (SCULPT_vertex_is_boundary(ss, ni.vertex)) { + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } } else { /* Interior vertices use all neighbors. */ - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } } @@ -73,13 +75,13 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], /* Do not modify corner vertices. */ if (neighbor_count <= 2 && is_boundary) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); return; } /* Avoid division by 0 when there are no neighbors. */ if (total == 0) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); return; } @@ -134,14 +136,14 @@ void SCULPT_bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert /* Generic functions for laplacian smoothing. These functions do not take boundary vertices into * account. */ -void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int index) +void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], PBVHVertRef vertex) { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.vertex)); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -150,18 +152,18 @@ void SCULPT_neighbor_coords_average(SculptSession *ss, float result[3], int inde mul_v3_v3fl(result, avg, 1.0f / total); } else { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + copy_v3_v3(result, SCULPT_vertex_co_get(ss, vertex)); } } -float SCULPT_neighbor_mask_average(SculptSession *ss, int index) +float SCULPT_neighbor_mask_average(SculptSession *ss, PBVHVertRef vertex) { float avg = 0.0f; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - avg += SCULPT_vertex_mask_get(ss, ni.index); + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { + avg += SCULPT_vertex_mask_get(ss, ni.vertex); total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); @@ -169,19 +171,19 @@ float SCULPT_neighbor_mask_average(SculptSession *ss, int index) if (total > 0) { return avg / total; } - return SCULPT_vertex_mask_get(ss, index); + return SCULPT_vertex_mask_get(ss, vertex); } -void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index) +void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], PBVHVertRef vertex) { float avg[4] = {0.0f, 0.0f, 0.0f, 0.0f}; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { float tmp[4] = {0}; - SCULPT_vertex_color_get(ss, ni.index, tmp); + SCULPT_vertex_color_get(ss, ni.vertex, tmp); add_v4_v4(avg, tmp); total++; @@ -192,7 +194,7 @@ void SCULPT_neighbor_color_average(SculptSession *ss, float result[4], int index mul_v4_v4fl(result, avg, 1.0f / total); } else { - SCULPT_vertex_color_get(ss, index, result); + SCULPT_vertex_color_get(ss, vertex, result); } } @@ -227,7 +229,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp[3]; @@ -235,7 +237,7 @@ static void do_enhance_details_brush_task_cb_ex(void *__restrict userdata, SCULPT_clip(sd, ss, vd.co, disp); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -258,9 +260,11 @@ static void SCULPT_enhance_details_brush(Sculpt *sd, totvert, sizeof(float[3]), "details directions"); for (int i = 0; i < totvert; i++) { + PBVHVertRef vertex = BKE_pbvh_index_to_vertex(ss->pbvh, i); + float avg[3]; - SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + SCULPT_neighbor_coords_average(ss, avg, vertex); + sub_v3_v3v3(ss->cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, vertex)); } } @@ -309,22 +313,22 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, vd.no, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), - vd.index, + vd.vertex, thread_id); if (smooth_mask) { - float val = SCULPT_neighbor_mask_average(ss, vd.index) - *vd.mask; + float val = SCULPT_neighbor_mask_average(ss, vd.vertex) - *vd.mask; val *= fade * bstrength; *vd.mask += val; CLAMP(*vd.mask, 0.0f, 1.0f); } else { float avg[3], val[3]; - SCULPT_neighbor_coords_average_interior(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.vertex); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } } @@ -403,13 +407,15 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, float *disp, const float co[3], float (*laplacian_disp)[3], - const int v_index, + const PBVHVertRef vertex, const float origco[3], const float alpha) { float laplacian_smooth_co[3]; float weigthed_o[3], weigthed_q[3], d[3]; - SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, v_index); + int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + + SCULPT_neighbor_coords_average(ss, laplacian_smooth_co, vertex); mul_v3_v3fl(weigthed_o, origco, alpha); mul_v3_v3fl(weigthed_q, co, 1.0f - alpha); @@ -422,7 +428,7 @@ void SCULPT_surface_smooth_laplacian_step(SculptSession *ss, void SCULPT_surface_smooth_displace_step(SculptSession *ss, float *co, float (*laplacian_disp)[3], - const int v_index, + const PBVHVertRef vertex, const float beta, const float fade) { @@ -430,12 +436,15 @@ void SCULPT_surface_smooth_displace_step(SculptSession *ss, float b_current_vertex[3]; int total = 0; SculptVertexNeighborIter ni; - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_index, ni) { + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vertex, ni) { add_v3_v3(b_avg, laplacian_disp[ni.index]); total++; } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { + int v_index = BKE_pbvh_vertex_to_index(ss->pbvh, vertex); + mul_v3_v3fl(b_current_vertex, b_avg, (1.0f - beta) / total); madd_v3_v3fl(b_current_vertex, laplacian_disp[v_index], beta); mul_v3_fl(b_current_vertex, clamp_f(fade, 0.0f, 1.0f)); @@ -474,15 +483,15 @@ static void SCULPT_do_surface_smooth_brush_laplacian_task_cb_ex( vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); float disp[3]; SCULPT_surface_smooth_laplacian_step( - ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, orig_data.co, alpha); + ss, disp, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, orig_data.co, alpha); madd_v3_v3fl(vd.co, disp, clamp_f(fade, 0.0f, 1.0f)); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -515,10 +524,10 @@ static void SCULPT_do_surface_smooth_brush_displace_task_cb_ex( vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, - vd.index, + vd.vertex, thread_id); SCULPT_surface_smooth_displace_step( - ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.index, beta, fade); + ss, vd.co, ss->cache->surface_smooth_laplacian_disp, vd.vertex, beta, fade); } BKE_pbvh_vertex_iter_end; } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index 2a7c6c8d0e0..7207e6c35d4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -179,7 +179,7 @@ static void sculpt_transform_task_cb(void *__restrict userdata, add_v3_v3v3(vd.co, start_co, disp); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; @@ -253,7 +253,7 @@ static void sculpt_elastic_transform_task_cb(void *__restrict userdata, copy_v3_v3(proxy[vd.i], final_disp); if (vd.mvert) { - BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.index); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } BKE_pbvh_vertex_iter_end; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index d37d0d2681f..04b2b2f04bf 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -297,20 +297,20 @@ static bool sculpt_undo_restore_coords(bContext *C, Depsgraph *depsgraph, Sculpt if (ss->deform_modifiers_active) { for (int i = 0; i < unode->totvert; i++) { sculpt_undo_restore_deformed(ss, unode, i, index[i], mvert[index[i]].co); - BKE_pbvh_vert_tag_update_normal(ss->pbvh, index[i]); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); } } else { for (int i = 0; i < unode->totvert; i++) { swap_v3_v3(mvert[index[i]].co, unode->orig_co[i]); - BKE_pbvh_vert_tag_update_normal(ss->pbvh, index[i]); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); } } } else { for (int i = 0; i < unode->totvert; i++) { swap_v3_v3(mvert[index[i]].co, unode->co[i]); - BKE_pbvh_vert_tag_update_normal(ss->pbvh, index[i]); + BKE_pbvh_vert_tag_update_normal(ss->pbvh, BKE_pbvh_make_vref(index[i])); } } } @@ -346,14 +346,13 @@ static bool sculpt_undo_restore_hidden(bContext *C, SculptUndoNode *unode, bool SculptSession *ss = ob->sculpt; SubdivCCG *subdiv_ccg = ss->subdiv_ccg; - if (unode->maxvert) { - MVert *mvert = ss->mvert; + bool *hide_vert = BKE_pbvh_get_vert_hide_for_write(ss->pbvh); + if (unode->maxvert) { for (int i = 0; i < unode->totvert; i++) { - MVert *v = &mvert[unode->index[i]]; - if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != ((v->flag & ME_HIDE) != 0)) { + if ((BLI_BITMAP_TEST(unode->vert_hidden, i) != 0) != hide_vert[i]) { BLI_BITMAP_FLIP(unode->vert_hidden, i); - v->flag ^= ME_HIDE; + hide_vert[unode->index[i]] = !hide_vert[i]; modified_vertices[unode->index[i]] = true; } } @@ -1247,6 +1246,11 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) PBVH *pbvh = ob->sculpt->pbvh; PBVHNode *node = unode->node; + const bool *hide_vert = BKE_pbvh_get_vert_hide(pbvh); + if (hide_vert == NULL) { + return; + } + if (unode->grids) { /* Already stored during allocation. */ } @@ -1258,7 +1262,7 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert); BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); for (int i = 0; i < allvert; i++) { - BLI_BITMAP_SET(unode->vert_hidden, i, mvert[vert_indices[i]].flag & ME_HIDE); + BLI_BITMAP_SET(unode->vert_hidden, i, hide_vert[vert_indices[i]]); } } } diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index dfa85e8e56d..14b06f888fe 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -414,9 +414,8 @@ static void uv_sculpt_stroke_exit(bContext *C, wmOperator *op) if (data->timer) { WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), data->timer); } - if (data->elementMap) { - BM_uv_element_map_free(data->elementMap); - } + BM_uv_element_map_free(data->elementMap); + data->elementMap = NULL; MEM_SAFE_FREE(data->uv); MEM_SAFE_FREE(data->uvedges); if (data->initial_stroke) { @@ -435,7 +434,7 @@ static int uv_element_offset_from_face_get( if (!element || (doIslands && element->island != island_index)) { return -1; } - return element - map->buf; + return element - map->storage; } static uint uv_edge_hash(const void *key) @@ -469,7 +468,6 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm BKE_curvemapping_init(ts->uvsculpt->paint.brush->curve); if (data) { - int counter = 0, i; ARegion *region = CTX_wm_region(C); float co[2]; BMFace *efa; @@ -492,13 +490,12 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm data->uvsculpt = &ts->uvsculpt->paint; - if (do_island_optimization) { - /* We will need island information */ - data->elementMap = BM_uv_element_map_create(bm, scene, false, true, true); - } - else { - data->elementMap = BM_uv_element_map_create(bm, scene, false, true, false); - } + /* Winding was added to island detection in 5197aa04c6bd + * However the sculpt tools can flip faces, potentially creating orphaned islands. + * See T100132 */ + bool use_winding = false; + data->elementMap = BM_uv_element_map_create( + bm, scene, false, use_winding, do_island_optimization); if (!data->elementMap) { uv_sculpt_stroke_exit(C, op); @@ -519,20 +516,18 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm } /* Count 'unique' UV's */ - for (i = 0; i < data->elementMap->totalUVs; i++) { - if (data->elementMap->buf[i].separate && - (!do_island_optimization || data->elementMap->buf[i].island == island_index)) { - counter++; - } + int unique_uvs = data->elementMap->total_unique_uvs; + if (do_island_optimization) { + unique_uvs = data->elementMap->island_total_unique_uvs[island_index]; } /* Allocate the unique uv buffers */ - data->uv = MEM_mallocN(sizeof(*data->uv) * counter, "uv_brush_unique_uvs"); - uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->totalUVs, + data->uv = MEM_mallocN(sizeof(*data->uv) * unique_uvs, "uv_brush_unique_uvs"); + uniqueUv = MEM_mallocN(sizeof(*uniqueUv) * data->elementMap->total_uvs, "uv_brush_unique_uv_map"); edgeHash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "uv_brush_edge_hash"); /* we have at most totalUVs edges */ - edges = MEM_mallocN(sizeof(*edges) * data->elementMap->totalUVs, "uv_brush_all_edges"); + edges = MEM_mallocN(sizeof(*edges) * data->elementMap->total_uvs, "uv_brush_all_edges"); if (!data->uv || !uniqueUv || !edgeHash || !edges) { MEM_SAFE_FREE(edges); MEM_SAFE_FREE(uniqueUv); @@ -543,12 +538,12 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm return NULL; } - data->totalUniqueUvs = counter; - /* So that we can use this as index for the UvElements */ - counter = -1; + data->totalUniqueUvs = unique_uvs; + /* Index for the UvElements. */ + int counter = -1; /* initialize the unique UVs */ - for (i = 0; i < bm->totvert; i++) { - UvElement *element = data->elementMap->vert[i]; + for (int i = 0; i < bm->totvert; i++) { + UvElement *element = data->elementMap->vertex[i]; for (; element; element = element->next) { if (element->separate) { if (do_island_optimization && (element->island != island_index)) { @@ -568,9 +563,10 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm data->uv[counter].uv = luv->uv; } /* Pointer arithmetic to the rescue, as always :). */ - uniqueUv[element - data->elementMap->buf] = counter; + uniqueUv[element - data->elementMap->storage] = counter; } } + BLI_assert(counter + 1 == unique_uvs); /* Now, on to generate our uv connectivity data */ counter = 0; @@ -628,11 +624,13 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm } /* fill the edges with data */ - i = 0; - GHASH_ITER (gh_iter, edgeHash) { - data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); + { + int i = 0; + GHASH_ITER (gh_iter, edgeHash) { + data->uvedges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); + } + data->totalUvEdges = BLI_ghash_len(edgeHash); } - data->totalUvEdges = BLI_ghash_len(edgeHash); /* cleanup temporary stuff */ BLI_ghash_free(edgeHash, NULL, NULL); @@ -640,7 +638,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm /* transfer boundary edge property to UV's */ if (ts->uv_sculpt_settings & UV_SCULPT_LOCK_BORDERS) { - for (i = 0; i < data->totalUvEdges; i++) { + for (int i = 0; i < data->totalUvEdges; i++) { if (!data->uvedges[i].flag) { data->uv[data->uvedges[i].uv1].flag |= MARK_BOUNDARY; data->uv[data->uvedges[i].uv2].flag |= MARK_BOUNDARY; @@ -687,7 +685,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm counter = 0; - for (i = 0; i < data->totalUniqueUvs; i++) { + for (int i = 0; i < data->totalUniqueUvs; i++) { float dist, diff[2]; if (data->uv[i].flag & MARK_BOUNDARY) { continue; diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index d1a8592ae9d..ed9d86e1a4e 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -919,7 +919,7 @@ static const EnumPropertyItem prop_column_select_types[] = { /* ------------------- */ /* Selects all visible keyframes between the specified markers */ -/* TODO(campbell): this is almost an _exact_ duplicate of a function of the same name in +/* TODO(@campbellbarton): this is almost an _exact_ duplicate of a function of the same name in * graph_select.c should de-duplicate. */ static void markers_selectkeys_between(bAnimContext *ac) { diff --git a/source/blender/editors/space_buttons/CMakeLists.txt b/source/blender/editors/space_buttons/CMakeLists.txt index 7d4f38b1841..b509eae8ea6 100644 --- a/source/blender/editors/space_buttons/CMakeLists.txt +++ b/source/blender/editors/space_buttons/CMakeLists.txt @@ -11,6 +11,7 @@ set(INC ../../windowmanager ../../../../intern/glew-mx ../../../../intern/guardedalloc + ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) diff --git a/source/blender/editors/space_graph/graph_select.c b/source/blender/editors/space_graph/graph_select.c index a36bd5c1461..0ce3e1a797a 100644 --- a/source/blender/editors/space_graph/graph_select.c +++ b/source/blender/editors/space_graph/graph_select.c @@ -1128,7 +1128,7 @@ static const EnumPropertyItem prop_column_select_types[] = { /* ------------------- */ /* Selects all visible keyframes between the specified markers */ -/* TODO(campbell): this is almost an _exact_ duplicate of a function of the same name in +/* TODO(@campbellbarton): this is almost an _exact_ duplicate of a function of the same name in * action_select.c should de-duplicate. */ static void markers_selectkeys_between(bAnimContext *ac) { diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 0a774ee679c..2109d3f9701 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -869,7 +869,8 @@ void uiTemplateImage(uiLayout *layout, uiItemS(col); uiItemR(col, &imaptr, "generated_type", UI_ITEM_R_EXPAND, IFACE_("Type"), ICON_NONE); - if (ima->gen_type == IMA_GENTYPE_BLANK) { + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + if (base_tile->gen_type == IMA_GENTYPE_BLANK) { uiItemR(col, &imaptr, "generated_color", 0, NULL, ICON_NONE); } } @@ -1211,6 +1212,11 @@ void uiTemplateImageInfo(uiLayout *layout, bContext *C, Image *ima, ImageUser *i ofs += BLI_strncpy_rlen(str + ofs, TIP_(" + Z"), len - ofs); } + eGPUTextureFormat texture_format = IMB_gpu_get_texture_format(ibuf, + ima->flag & IMA_HIGH_BITDEPTH); + const char *texture_format_description = GPU_texture_format_description(texture_format); + ofs += BLI_snprintf_rlen(str + ofs, len - ofs, TIP_(", %s"), texture_format_description); + uiItemL(col, str, ICON_NONE); } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 4036f859231..78aaf957a87 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -3847,15 +3847,16 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot) static bool do_fill_tile(PointerRNA *ptr, Image *ima, ImageTile *tile) { - float color[4]; - RNA_float_get_array(ptr, "color", color); - int gen_type = RNA_enum_get(ptr, "generated_type"); - int width = RNA_int_get(ptr, "width"); - int height = RNA_int_get(ptr, "height"); + RNA_float_get_array(ptr, "color", tile->gen_color); + tile->gen_type = RNA_enum_get(ptr, "generated_type"); + tile->gen_x = RNA_int_get(ptr, "width"); + tile->gen_y = RNA_int_get(ptr, "height"); bool is_float = RNA_boolean_get(ptr, "float"); - int planes = RNA_boolean_get(ptr, "alpha") ? 32 : 24; - return BKE_image_fill_tile(ima, tile, width, height, color, gen_type, planes, is_float); + tile->gen_flag = is_float ? IMA_GEN_FLOAT : 0; + tile->gen_depth = RNA_boolean_get(ptr, "alpha") ? 32 : 24; + + return BKE_image_fill_tile(ima, tile); } static void draw_fill_tile(PointerRNA *ptr, uiLayout *layout) diff --git a/source/blender/editors/space_image/image_undo.cc b/source/blender/editors/space_image/image_undo.cc index 0fe0414c177..065641c4051 100644 --- a/source/blender/editors/space_image/image_undo.cc +++ b/source/blender/editors/space_image/image_undo.cc @@ -432,7 +432,7 @@ static void utile_decref(UndoImageTile *utile) /** \name Image Undo Buffer * \{ */ -typedef struct UndoImageBuf { +struct UndoImageBuf { struct UndoImageBuf *next, *prev; /** @@ -454,10 +454,8 @@ typedef struct UndoImageBuf { struct { short source; bool use_float; - char gen_type; } image_state; - -} UndoImageBuf; +}; static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf) { @@ -474,7 +472,6 @@ static UndoImageBuf *ubuf_from_image_no_tiles(Image *image, const ImBuf *ibuf) MEM_callocN(sizeof(*ubuf->tiles) * ubuf->tiles_len, __func__)); BLI_strncpy(ubuf->ibuf_name, ibuf->name, sizeof(ubuf->ibuf_name)); - ubuf->image_state.gen_type = image->gen_type; ubuf->image_state.source = image->source; ubuf->image_state.use_float = ibuf->rect_float != nullptr; @@ -552,7 +549,7 @@ static void ubuf_free(UndoImageBuf *ubuf) /** \name Image Undo Handle * \{ */ -typedef struct UndoImageHandle { +struct UndoImageHandle { struct UndoImageHandle *next, *prev; /** Each undo handle refers to a single image which may have multiple buffers. */ @@ -567,8 +564,7 @@ typedef struct UndoImageHandle { * List of #UndoImageBuf's to support multiple buffers per image. */ ListBase buffers; - -} UndoImageHandle; +}; static void uhandle_restore_list(ListBase *undo_handles, bool use_init) { diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc index 29a7eb150a1..e41ff02254b 100644 --- a/source/blender/editors/space_info/info_stats.cc +++ b/source/blender/editors/space_info/info_stats.cc @@ -439,14 +439,7 @@ static void stats_update(Depsgraph *depsgraph, } else if (ob && (ob->mode & OB_MODE_SCULPT)) { /* Sculpt Mode. */ - if (stats_is_object_dynamic_topology_sculpt(ob)) { - /* Dynamic topology. Do not count all vertices, - * dynamic topology stats are initialized later as part of sculpt stats. */ - } - else { - /* When dynamic topology is not enabled both sculpt stats and scene stats are collected. */ - stats_object_sculpt(ob, stats); - } + stats_object_sculpt(ob, stats); } else { /* Objects. */ diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index badcccca87b..26fddda8c22 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -50,8 +50,8 @@ set(LIB bf_editor_screen ) -if(WITH_COMPOSITOR) - add_definitions(-DWITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_OPENIMAGEDENOISE) diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc index c524de2c55d..9014e36c4e2 100644 --- a/source/blender/editors/space_node/link_drag_search.cc +++ b/source/blender/editors/space_node/link_drag_search.cc @@ -232,6 +232,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2) PointerRNA ptr; WM_operator_properties_create_ptr(&ptr, ot); RNA_boolean_set(&ptr, "view2d_edge_pan", true); + RNA_boolean_set(&ptr, "remove_on_cancel", true); WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr, nullptr); WM_operator_properties_free(&ptr); } diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index a89b5444a4d..0d498d07aff 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -49,29 +49,48 @@ namespace blender::ed::space_node { /** \name Utilities * \{ */ -bNode *node_add_node(const bContext &C, const char *idname, int type, float locx, float locy) +static void position_node_based_on_mouse(bNode &node, const float2 &location) +{ + node.locx = location.x - NODE_DY * 1.5f / UI_DPI_FAC; + node.locy = location.y + NODE_DY * 0.5f / UI_DPI_FAC; +} + +bNode *add_node(const bContext &C, const StringRef idname, const float2 &location) { SpaceNode &snode = *CTX_wm_space_node(&C); Main &bmain = *CTX_data_main(&C); - bNode *node = nullptr; node_deselect_all(snode); - if (idname) { - node = nodeAddNode(&C, snode.edittree, idname); - } - else { - node = nodeAddStaticNode(&C, snode.edittree, type); - } + const std::string idname_str = idname; + + bNode *node = nodeAddNode(&C, snode.edittree, idname_str.c_str()); BLI_assert(node && node->typeinfo); - /* Position mouse in node header. */ - node->locx = locx - NODE_DY * 1.5f / UI_DPI_FAC; - node->locy = locy + NODE_DY * 0.5f / UI_DPI_FAC; + position_node_based_on_mouse(*node, location); nodeSetSelected(node, true); + ED_node_set_active(&bmain, &snode, snode.edittree, node, nullptr); + + ED_node_tree_propagate_change(&C, &bmain, snode.edittree); + return node; +} + +bNode *add_static_node(const bContext &C, int type, const float2 &location) +{ + SpaceNode &snode = *CTX_wm_space_node(&C); + Main &bmain = *CTX_data_main(&C); + + node_deselect_all(snode); + + bNode *node = nodeAddStaticNode(&C, snode.edittree, type); + BLI_assert(node && node->typeinfo); + position_node_based_on_mouse(*node, location); + + nodeSetSelected(node, true); ED_node_set_active(&bmain, &snode, snode.edittree, node, nullptr); + ED_node_tree_propagate_change(&C, &bmain, snode.edittree); return node; } @@ -338,9 +357,9 @@ static int node_add_group_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); SpaceNode *snode = CTX_wm_space_node(C); bNodeTree *ntree = snode->edittree; - bNodeTree *node_group; - if (!(node_group = node_add_group_get_and_poll_group_node_tree(bmain, op, ntree))) { + bNodeTree *node_group = node_add_group_get_and_poll_group_node_tree(bmain, op, ntree); + if (!node_group) { return OPERATOR_CANCELLED; } @@ -352,12 +371,7 @@ static int node_add_group_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - bNode *group_node = node_add_node(*C, - node_idname, - (node_group->type == NTREE_CUSTOM) ? NODE_CUSTOM_GROUP : - NODE_GROUP, - snode->runtime->cursor[0], - snode->runtime->cursor[1]); + bNode *group_node = add_node(*C, node_idname, snode->runtime->cursor); if (!group_node) { BKE_report(op->reports, RPT_WARNING, "Could not add node group"); return OPERATOR_CANCELLED; @@ -445,8 +459,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - bNode *object_node = node_add_node( - *C, nullptr, GEO_NODE_OBJECT_INFO, snode->runtime->cursor[0], snode->runtime->cursor[1]); + bNode *object_node = add_static_node(*C, GEO_NODE_OBJECT_INFO, snode->runtime->cursor); if (!object_node) { BKE_report(op->reports, RPT_WARNING, "Could not add node object"); return OPERATOR_CANCELLED; @@ -522,7 +535,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); - bNodeTree *ntree = snode.edittree; + bNodeTree &ntree = *snode.edittree; Collection *collection = reinterpret_cast<Collection *>( WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_GR)); @@ -533,8 +546,7 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - bNode *collection_node = node_add_node( - *C, nullptr, GEO_NODE_COLLECTION_INFO, snode.runtime->cursor[0], snode.runtime->cursor[1]); + bNode *collection_node = add_static_node(*C, GEO_NODE_COLLECTION_INFO, snode.runtime->cursor); if (!collection_node) { BKE_report(op->reports, RPT_WARNING, "Could not add node collection"); return OPERATOR_CANCELLED; @@ -550,8 +562,8 @@ static int node_add_collection_exec(bContext *C, wmOperator *op) socket_data->value = collection; id_us_plus(&collection->id); - nodeSetActive(ntree, collection_node); - ED_node_tree_propagate_change(C, bmain, ntree); + nodeSetActive(&ntree, collection_node); + ED_node_tree_propagate_change(C, bmain, &ntree); DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; @@ -617,11 +629,9 @@ static int node_add_file_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); - bNode *node; - Image *ima; int type = 0; - ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM); + Image *ima = (Image *)WM_operator_drop_load_path(C, op, ID_IM); if (!ima) { return OPERATOR_CANCELLED; } @@ -645,7 +655,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - node = node_add_node(*C, nullptr, type, snode.runtime->cursor[0], snode.runtime->cursor[1]); + bNode *node = add_static_node(*C, type, snode.runtime->cursor); if (!node) { BKE_report(op->reports, RPT_WARNING, "Could not add an image node"); @@ -739,7 +749,6 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceNode &snode = *CTX_wm_space_node(C); - bNode *node; ID *mask = WM_operator_properties_id_lookup_from_name_or_session_uuid(bmain, op->ptr, ID_MSK); if (!mask) { @@ -748,8 +757,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - node = node_add_node( - *C, nullptr, CMP_NODE_MASK, snode.runtime->cursor[0], snode.runtime->cursor[1]); + bNode *node = add_static_node(*C, CMP_NODE_MASK, snode.runtime->cursor); if (!node) { BKE_report(op->reports, RPT_WARNING, "Could not add a mask node"); diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc index a926f7e8917..bb520c0537e 100644 --- a/source/blender/editors/space_node/node_group.cc +++ b/source/blender/editors/space_node/node_group.cc @@ -45,7 +45,12 @@ #include "UI_resources.h" #include "NOD_common.h" +#include "NOD_composite.h" +#include "NOD_geometry.h" +#include "NOD_shader.h" #include "NOD_socket.h" +#include "NOD_texture.h" + #include "node_intern.hh" /* own include */ namespace blender::ed::space_node { @@ -100,16 +105,16 @@ const char *node_group_idname(bContext *C) SpaceNode *snode = CTX_wm_space_node(C); if (ED_node_is_shader(snode)) { - return "ShaderNodeGroup"; + return ntreeType_Shader->group_idname; } if (ED_node_is_compositor(snode)) { - return "CompositorNodeGroup"; + return ntreeType_Composite->group_idname; } if (ED_node_is_texture(snode)) { - return "TextureNodeGroup"; + return ntreeType_Texture->group_idname; } if (ED_node_is_geometry(snode)) { - return "GeometryNodeGroup"; + return ntreeType_Geometry->group_idname; } return ""; diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh index 924537d0e8a..81c2bc0e962 100644 --- a/source/blender/editors/space_node/node_intern.hh +++ b/source/blender/editors/space_node/node_intern.hh @@ -245,12 +245,9 @@ void draw_nodespace_back_pix(const bContext &C, /* node_add.cc */ -/** - * XXX Does some additional initialization on top of #nodeAddNode - * Can be used with both custom and static nodes, - * if `idname == nullptr` the static int type will be used instead. - */ -bNode *node_add_node(const bContext &C, const char *idname, int type, float locx, float locy); +bNode *add_node(const bContext &C, StringRef idname, const float2 &location); +bNode *add_static_node(const bContext &C, int type, const float2 &location); + void NODE_OT_add_reroute(wmOperatorType *ot); void NODE_OT_add_group(wmOperatorType *ot); void NODE_OT_add_object(wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index e10bedb18f4..d911e53be7f 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -639,8 +639,8 @@ static int link_socket_to_viewer(const bContext &C, if (viewer_bnode == nullptr) { /* Create a new viewer node if none exists. */ const int viewer_type = get_default_viewer_type(&C); - viewer_bnode = node_add_node( - C, nullptr, viewer_type, bsocket_to_view.locx + 100, bsocket_to_view.locy); + const float2 location{bsocket_to_view.locx + 100, bsocket_to_view.locy}; + viewer_bnode = add_static_node(C, viewer_type, location); if (viewer_bnode == nullptr) { return OPERATOR_CANCELLED; } @@ -1654,7 +1654,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) SpaceNode &snode = *CTX_wm_space_node(C); bNodeTree &ntree = *snode.edittree; - /* XXX save selection: node_add_node call below sets the new frame as single + /* XXX save selection: add_static_node call below sets the new frame as single * active+selected node */ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { if (node->flag & NODE_SELECT) { @@ -1665,7 +1665,7 @@ static int node_join_exec(bContext *C, wmOperator *UNUSED(op)) } } - bNode *frame = node_add_node(*C, nullptr, NODE_FRAME, 0.0f, 0.0f); + bNode *frame = add_static_node(*C, NODE_FRAME, float2(0)); /* reset tags */ LISTBASE_FOREACH (bNode *, node, &ntree.nodes) { diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 78ec057f921..b9f79303a06 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -48,11 +48,11 @@ set(SRC tree/tree_element_anim_data.cc tree/tree_element_collection.cc tree/tree_element_driver.cc - tree/tree_element_label.cc tree/tree_element_gpencil_layer.cc tree/tree_element_id.cc tree/tree_element_id_library.cc tree/tree_element_id_scene.cc + tree/tree_element_label.cc tree/tree_element_nla.cc tree/tree_element_overrides.cc tree/tree_element_rna.cc @@ -68,11 +68,11 @@ set(SRC tree/tree_element_anim_data.hh tree/tree_element_collection.hh tree/tree_element_driver.hh - tree/tree_element_label.hh tree/tree_element_gpencil_layer.hh tree/tree_element_id.hh tree/tree_element_id_library.hh tree/tree_element_id_scene.hh + tree/tree_element_label.hh tree/tree_element_nla.hh tree/tree_element_overrides.hh tree/tree_element_rna.hh diff --git a/source/blender/editors/space_outliner/outliner_collections.cc b/source/blender/editors/space_outliner/outliner_collections.cc index b29d20f9f9c..02d54e4f702 100644 --- a/source/blender/editors/space_outliner/outliner_collections.cc +++ b/source/blender/editors/space_outliner/outliner_collections.cc @@ -72,7 +72,7 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te) } if (tselem->type == TSE_LAYER_COLLECTION) { - LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata); + LayerCollection *lc = static_cast<LayerCollection *>(te->directdata); return lc->collection; } if (ELEM(tselem->type, TSE_SCENE_COLLECTION_BASE, TSE_VIEW_COLLECTION_BASE)) { @@ -88,7 +88,7 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te) TreeTraversalAction outliner_find_selected_collections(TreeElement *te, void *customdata) { - struct IDsSelectedData *data = reinterpret_cast<IDsSelectedData *>(customdata); + struct IDsSelectedData *data = static_cast<IDsSelectedData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); if (outliner_is_collection_tree_element(te)) { @@ -105,7 +105,7 @@ TreeTraversalAction outliner_find_selected_collections(TreeElement *te, void *cu TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata) { - struct IDsSelectedData *data = reinterpret_cast<IDsSelectedData *>(customdata); + struct IDsSelectedData *data = static_cast<IDsSelectedData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); if (outliner_is_collection_tree_element(te)) { @@ -184,7 +184,7 @@ struct CollectionNewData { static TreeTraversalAction collection_find_selected_to_add(TreeElement *te, void *customdata) { - struct CollectionNewData *data = reinterpret_cast<CollectionNewData *>(customdata); + struct CollectionNewData *data = static_cast<CollectionNewData *>(customdata); Collection *collection = outliner_collection_from_tree_element(te); if (!collection) { @@ -286,7 +286,7 @@ struct CollectionEditData { static TreeTraversalAction collection_find_data_to_edit(TreeElement *te, void *customdata) { - CollectionEditData *data = reinterpret_cast<CollectionEditData *>(customdata); + CollectionEditData *data = static_cast<CollectionEditData *>(customdata); Collection *collection = outliner_collection_from_tree_element(te); if (!collection) { @@ -339,7 +339,7 @@ void outliner_collection_delete( /* Effectively delete the collections. */ GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); /* Test in case collection got deleted as part of another one. */ @@ -444,12 +444,12 @@ struct CollectionObjectsSelectData { static TreeTraversalAction outliner_find_first_selected_layer_collection(TreeElement *te, void *customdata) { - CollectionObjectsSelectData *data = reinterpret_cast<CollectionObjectsSelectData *>(customdata); + CollectionObjectsSelectData *data = static_cast<CollectionObjectsSelectData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); switch (tselem->type) { case TSE_LAYER_COLLECTION: - data->layer_collection = reinterpret_cast<LayerCollection *>(te->directdata); + data->layer_collection = static_cast<LayerCollection *>(te->directdata); return TRAVERSE_BREAK; case TSE_R_LAYER: case TSE_SCENE_COLLECTION_BASE: @@ -538,7 +538,7 @@ struct CollectionDuplicateData { static TreeTraversalAction outliner_find_first_selected_collection(TreeElement *te, void *customdata) { - CollectionDuplicateData *data = reinterpret_cast<CollectionDuplicateData *>(customdata); + CollectionDuplicateData *data = static_cast<CollectionDuplicateData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); switch (tselem->type) { @@ -701,7 +701,7 @@ static int collection_link_exec(bContext *C, wmOperator *op) /* Effectively link the collections. */ GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); BKE_collection_child_add(bmain, active_collection, collection); id_fake_user_clear(&collection->id); @@ -762,7 +762,7 @@ static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op)) GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); while (BKE_collection_cycle_find(active_lc->collection, collection)) { @@ -772,7 +772,7 @@ static int collection_instance_exec(bContext *C, wmOperator *UNUSED(op)) /* Effectively instance the collections. */ GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); Object *ob = ED_object_add_type( C, OB_EMPTY, collection->id.name + 2, scene->cursor.location, nullptr, false, 0); @@ -814,14 +814,14 @@ void OUTLINER_OT_collection_instance(wmOperatorType *ot) static TreeTraversalAction layer_collection_find_data_to_edit(TreeElement *te, void *customdata) { - CollectionEditData *data = reinterpret_cast<CollectionEditData *>(customdata); + CollectionEditData *data = static_cast<CollectionEditData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); if (!(tselem && tselem->type == TSE_LAYER_COLLECTION)) { return TRAVERSE_CONTINUE; } - LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata); + LayerCollection *lc = static_cast<LayerCollection *>(te->directdata); if (lc->collection->flag & COLLECTION_IS_MASTER) { /* skip - showing warning/error message might be misleading @@ -862,7 +862,7 @@ static bool collections_view_layer_poll(bContext *C, bool clear, int flag) GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *lc = reinterpret_cast<LayerCollection *>( + LayerCollection *lc = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); if (clear && (lc->flag & flag)) { @@ -934,7 +934,7 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op) GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *lc = reinterpret_cast<LayerCollection *>( + LayerCollection *lc = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); BKE_layer_collection_set_flag(lc, flag, !clear); } @@ -1068,7 +1068,7 @@ static int collection_isolate_exec(bContext *C, wmOperator *op) GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); if (extend) { @@ -1168,7 +1168,7 @@ static int collection_visibility_exec(bContext *C, wmOperator *op) GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); BKE_layer_collection_set_visible(view_layer, layer_collection, show, is_inside); } @@ -1319,7 +1319,7 @@ static int collection_flag_exec(bContext *C, wmOperator *op) &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); Collection *collection = layer_collection->collection; if (!BKE_id_is_editable(bmain, &collection->id)) { @@ -1348,7 +1348,7 @@ static int collection_flag_exec(bContext *C, wmOperator *op) &data); GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - Collection *collection = reinterpret_cast<Collection *>( + Collection *collection = static_cast<Collection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); if (!BKE_id_is_editable(bmain, &collection->id)) { continue; @@ -1451,7 +1451,7 @@ struct OutlinerHideEditData { static TreeTraversalAction outliner_hide_find_data_to_edit(TreeElement *te, void *customdata) { - OutlinerHideEditData *data = reinterpret_cast<OutlinerHideEditData *>(customdata); + OutlinerHideEditData *data = static_cast<OutlinerHideEditData *>(customdata); TreeStoreElem *tselem = TREESTORE(te); if (tselem == nullptr) { @@ -1459,7 +1459,7 @@ static TreeTraversalAction outliner_hide_find_data_to_edit(TreeElement *te, void } if (tselem->type == TSE_LAYER_COLLECTION) { - LayerCollection *lc = reinterpret_cast<LayerCollection *>(te->directdata); + LayerCollection *lc = static_cast<LayerCollection *>(te->directdata); if (lc->collection->flag & COLLECTION_IS_MASTER) { /* Skip - showing warning/error message might be misleading @@ -1501,7 +1501,7 @@ static int outliner_hide_exec(bContext *C, wmOperator *UNUSED(op)) GSetIterator collections_to_edit_iter; GSET_ITER (collections_to_edit_iter, data.collections_to_edit) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( BLI_gsetIterator_getKey(&collections_to_edit_iter)); BKE_layer_collection_set_visible(view_layer, layer_collection, false, false); } @@ -1509,7 +1509,7 @@ static int outliner_hide_exec(bContext *C, wmOperator *UNUSED(op)) GSetIterator bases_to_edit_iter; GSET_ITER (bases_to_edit_iter, data.bases_to_edit) { - Base *base = reinterpret_cast<Base *>(BLI_gsetIterator_getKey(&bases_to_edit_iter)); + Base *base = static_cast<Base *>(BLI_gsetIterator_getKey(&bases_to_edit_iter)); base->flag |= BASE_HIDDEN; } BLI_gset_free(data.bases_to_edit, nullptr); @@ -1542,8 +1542,7 @@ static int outliner_unhide_all_exec(bContext *C, wmOperator *UNUSED(op)) ViewLayer *view_layer = CTX_data_view_layer(C); /* Unhide all the collections. */ - LayerCollection *lc_master = reinterpret_cast<LayerCollection *>( - view_layer->layer_collections.first); + LayerCollection *lc_master = static_cast<LayerCollection *>(view_layer->layer_collections.first); LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) { BKE_layer_collection_set_flag(lc_iter, LAYER_COLLECTION_HIDE, false); } diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.cc b/source/blender/editors/space_outliner/outliner_dragdrop.cc index 7435fa50a93..2fa512b4006 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.cc +++ b/source/blender/editors/space_outliner/outliner_dragdrop.cc @@ -144,7 +144,7 @@ static TreeElement *outliner_drop_insert_find(bContext *C, return te_hovered; } *r_insert_type = TE_INSERT_BEFORE; - return reinterpret_cast<TreeElement *>(te_hovered->subtree.first); + return static_cast<TreeElement *>(te_hovered->subtree.first); } *r_insert_type = TE_INSERT_AFTER; return te_hovered; @@ -159,8 +159,8 @@ static TreeElement *outliner_drop_insert_find(bContext *C, /* Mouse doesn't hover any item (ignoring x-axis), * so it's either above list bounds or below. */ - TreeElement *first = reinterpret_cast<TreeElement *>(space_outliner->tree.first); - TreeElement *last = reinterpret_cast<TreeElement *>(space_outliner->tree.last); + TreeElement *first = static_cast<TreeElement *>(space_outliner->tree.first); + TreeElement *last = static_cast<TreeElement *>(space_outliner->tree.last); if (view_mval[1] < last->ys) { *r_insert_type = TE_INSERT_AFTER; @@ -422,12 +422,12 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; } - ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); - wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); + ListBase *lb = static_cast<ListBase *>(event->customdata); + wmDrag *drag = static_cast<wmDrag *>(lb->first); parent_drop_set_parents(C, op->reports, - reinterpret_cast<wmDragID *>(drag->ids.first), + static_cast<wmDragID *>(drag->ids.first), par, PAR_OBJECT, event->modifier & KM_ALT); @@ -505,8 +505,8 @@ static int parent_clear_invoke(bContext *C, wmOperator *UNUSED(op), const wmEven return OPERATOR_CANCELLED; } - ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); - wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); + ListBase *lb = static_cast<ListBase *>(event->customdata); + wmDrag *drag = static_cast<wmDrag *>(lb->first); LISTBASE_FOREACH (wmDragID *, drag_id, &drag->ids) { if (GS(drag_id->id->name) == ID_OB) { @@ -849,7 +849,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) ARegion *region = CTX_wm_region(C); bool changed = outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false); - StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); + StackDropData *drop_data = static_cast<StackDropData *>(drag->poin); if (!drop_data) { return false; } @@ -887,7 +887,7 @@ static char *datastack_drop_tooltip(bContext *UNUSED(C), const int UNUSED(xy[2]), struct wmDropBox *UNUSED(drop)) { - StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); + StackDropData *drop_data = static_cast<StackDropData *>(drag->poin); switch (drop_data->drop_action) { case DATA_STACK_DROP_REORDER: return BLI_strdup(TIP_("Reorder")); @@ -965,14 +965,13 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data) case TSE_MODIFIER: if (drop_data->ob_parent->type == OB_GPENCIL && ob_dst->type == OB_GPENCIL) { ED_object_gpencil_modifier_copy_to_object( - ob_dst, reinterpret_cast<GpencilModifierData *>(drop_data->drag_directdata)); + ob_dst, static_cast<GpencilModifierData *>(drop_data->drag_directdata)); } else if (drop_data->ob_parent->type != OB_GPENCIL && ob_dst->type != OB_GPENCIL) { - ED_object_modifier_copy_to_object( - C, - ob_dst, - drop_data->ob_parent, - reinterpret_cast<ModifierData *>(drop_data->drag_directdata)); + ED_object_modifier_copy_to_object(C, + ob_dst, + drop_data->ob_parent, + static_cast<ModifierData *>(drop_data->drag_directdata)); } break; case TSE_CONSTRAINT: @@ -980,12 +979,12 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data) ED_object_constraint_copy_for_pose( bmain, ob_dst, - reinterpret_cast<bPoseChannel *>(drop_data->drop_te->directdata), - reinterpret_cast<bConstraint *>(drop_data->drag_directdata)); + static_cast<bPoseChannel *>(drop_data->drop_te->directdata), + static_cast<bConstraint *>(drop_data->drag_directdata)); } else { ED_object_constraint_copy_for_object( - bmain, ob_dst, reinterpret_cast<bConstraint *>(drop_data->drag_directdata)); + bmain, ob_dst, static_cast<bConstraint *>(drop_data->drag_directdata)); } break; case TSE_GPENCIL_EFFECT: { @@ -993,8 +992,7 @@ static void datastack_drop_copy(bContext *C, StackDropData *drop_data) return; } - ED_object_shaderfx_copy(ob_dst, - reinterpret_cast<ShaderFxData *>(drop_data->drag_directdata)); + ED_object_shaderfx_copy(ob_dst, static_cast<ShaderFxData *>(drop_data->drag_directdata)); break; } } @@ -1021,15 +1019,12 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa index = outliner_get_insert_index( drag_te, drop_te, insert_type, &ob->greasepencil_modifiers); ED_object_gpencil_modifier_move_to_index( - reports, - ob, - reinterpret_cast<GpencilModifierData *>(drop_data->drag_directdata), - index); + reports, ob, static_cast<GpencilModifierData *>(drop_data->drag_directdata), index); } else { index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->modifiers); ED_object_modifier_move_to_index( - reports, ob, reinterpret_cast<ModifierData *>(drop_data->drag_directdata), index); + reports, ob, static_cast<ModifierData *>(drop_data->drag_directdata), index); } break; case TSE_CONSTRAINT: @@ -1041,13 +1036,13 @@ static void datastack_drop_reorder(bContext *C, ReportList *reports, StackDropDa index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->constraints); } ED_object_constraint_move_to_index( - ob, reinterpret_cast<bConstraint *>(drop_data->drag_directdata), index); + ob, static_cast<bConstraint *>(drop_data->drag_directdata), index); break; case TSE_GPENCIL_EFFECT: index = outliner_get_insert_index(drag_te, drop_te, insert_type, &ob->shader_fx); ED_object_shaderfx_move_to_index( - reports, ob, reinterpret_cast<ShaderFxData *>(drop_data->drag_directdata), index); + reports, ob, static_cast<ShaderFxData *>(drop_data->drag_directdata), index); } } @@ -1057,9 +1052,9 @@ static int datastack_drop_invoke(bContext *C, wmOperator *op, const wmEvent *eve return OPERATOR_CANCELLED; } - ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); - wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); - StackDropData *drop_data = reinterpret_cast<StackDropData *>(drag->poin); + ListBase *lb = static_cast<ListBase *>(event->customdata); + wmDrag *drag = static_cast<wmDrag *>(lb->first); + StackDropData *drop_data = static_cast<StackDropData *>(drag->poin); switch (drop_data->drop_action) { case DATA_STACK_DROP_LINK: @@ -1143,7 +1138,7 @@ static bool collection_drop_init(bContext *C, wmDrag *drag, const int xy[2], Col return false; } - wmDragID *drag_id = reinterpret_cast<wmDragID *>(drag->ids.first); + wmDragID *drag_id = static_cast<wmDragID *>(drag->ids.first); if (drag_id == nullptr) { return false; } @@ -1300,8 +1295,8 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE return OPERATOR_CANCELLED; } - ListBase *lb = reinterpret_cast<ListBase *>(event->customdata); - wmDrag *drag = reinterpret_cast<wmDrag *>(lb->first); + ListBase *lb = static_cast<ListBase *>(event->customdata); + wmDrag *drag = static_cast<wmDrag *>(lb->first); CollectionDrop data; if (!collection_drop_init(C, drag, event->xy, &data)) { diff --git a/source/blender/editors/space_outliner/outliner_draw.cc b/source/blender/editors/space_outliner/outliner_draw.cc index ae9ffffd145..e67eab4e432 100644 --- a/source/blender/editors/space_outliner/outliner_draw.cc +++ b/source/blender/editors/space_outliner/outliner_draw.cc @@ -277,8 +277,8 @@ static void outliner_object_set_flag_recursive_fn(bContext *C, Object *ob_parent = ob ? ob : base->object; - for (Object *ob_iter = reinterpret_cast<Object *>(bmain->objects.first); ob_iter; - ob_iter = reinterpret_cast<Object *>(ob_iter->id.next)) { + for (Object *ob_iter = static_cast<Object *>(bmain->objects.first); ob_iter; + ob_iter = static_cast<Object *>(ob_iter->id.next)) { if (BKE_object_is_child_recursive(ob_parent, ob_iter)) { if (ob) { RNA_id_pointer_create(&ob_iter->id, &ptr); @@ -312,8 +312,8 @@ static void outliner_object_set_flag_recursive_fn(bContext *C, */ static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - Object *ob = reinterpret_cast<Object *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + Object *ob = static_cast<Object *>(poin); + char *propname = static_cast<char *>(poin2); outliner_object_set_flag_recursive_fn(C, nullptr, ob, propname); } @@ -322,8 +322,8 @@ static void outliner__object_set_flag_recursive_fn(bContext *C, void *poin, void */ static void outliner__base_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - Base *base = reinterpret_cast<Base *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + Base *base = static_cast<Base *>(poin); + char *propname = static_cast<char *>(poin2); outliner_object_set_flag_recursive_fn(C, base, nullptr, propname); } @@ -488,7 +488,7 @@ void outliner_collection_isolate_flag(Scene *scene, const bool is_hide = strstr(propname, "hide_") != nullptr; LayerCollection *top_layer_collection = layer_collection ? - reinterpret_cast<LayerCollection *>( + static_cast<LayerCollection *>( view_layer->layer_collections.first) : nullptr; Collection *top_collection = collection ? scene->master_collection : nullptr; @@ -559,7 +559,7 @@ void outliner_collection_isolate_flag(Scene *scene, else { CollectionParent *parent; Collection *child = collection; - while ((parent = reinterpret_cast<CollectionParent *>(child->parents.first))) { + while ((parent = static_cast<CollectionParent *>(child->parents.first))) { if (parent->collection->flag & COLLECTION_IS_MASTER) { break; } @@ -638,8 +638,8 @@ static void view_layer__layer_collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + LayerCollection *layer_collection = static_cast<LayerCollection *>(poin); + char *propname = static_cast<char *>(poin2); outliner_collection_set_flag_recursive_fn(C, layer_collection, nullptr, propname); } @@ -649,8 +649,8 @@ static void view_layer__layer_collection_set_flag_recursive_fn(bContext *C, */ static void view_layer__collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + LayerCollection *layer_collection = static_cast<LayerCollection *>(poin); + char *propname = static_cast<char *>(poin2); outliner_collection_set_flag_recursive_fn( C, layer_collection, layer_collection->collection, propname); } @@ -661,8 +661,8 @@ static void view_layer__collection_set_flag_recursive_fn(bContext *C, void *poin */ static void scenes__collection_set_flag_recursive_fn(bContext *C, void *poin, void *poin2) { - Collection *collection = reinterpret_cast<Collection *>(poin); - char *propname = reinterpret_cast<char *>(poin2); + Collection *collection = static_cast<Collection *>(poin); + char *propname = static_cast<char *>(poin2); outliner_collection_set_flag_recursive_fn(C, nullptr, collection, propname); } @@ -672,7 +672,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); struct wmMsgBus *mbus = CTX_wm_message_bus(C); BLI_mempool *ts = space_outliner->treestore; - TreeStoreElem *tselem = reinterpret_cast<TreeStoreElem *>(tsep); + TreeStoreElem *tselem = static_cast<TreeStoreElem *>(tsep); if (ts && tselem) { TreeElement *te = outliner_find_tree_element(&space_outliner->tree, tselem); @@ -737,7 +737,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) switch (tselem->type) { case TSE_DEFGROUP: { Object *ob = (Object *)tselem->id; - bDeformGroup *vg = reinterpret_cast<bDeformGroup *>(te->directdata); + bDeformGroup *vg = static_cast<bDeformGroup *>(te->directdata); BKE_object_defgroup_unique_name(vg, ob); WM_msg_publish_rna_prop(mbus, &ob->id, vg, VertexGroup, name); break; @@ -752,7 +752,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) case TSE_EBONE: { bArmature *arm = (bArmature *)tselem->id; if (arm->edbo) { - EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata); + EditBone *ebone = static_cast<EditBone *>(te->directdata); char newname[sizeof(ebone->name)]; /* restore bone name */ @@ -770,7 +770,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) outliner_viewcontext_init(C, &tvc); bArmature *arm = (bArmature *)tselem->id; - Bone *bone = reinterpret_cast<Bone *>(te->directdata); + Bone *bone = static_cast<Bone *>(te->directdata); char newname[sizeof(bone->name)]; /* always make current object active */ @@ -790,7 +790,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) Object *ob = (Object *)tselem->id; bArmature *arm = (bArmature *)ob->data; - bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata); + bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); char newname[sizeof(pchan->name)]; /* always make current pose-bone active */ @@ -801,15 +801,14 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) /* restore bone name */ BLI_strncpy(newname, pchan->name, sizeof(pchan->name)); BLI_strncpy(pchan->name, oldname, sizeof(pchan->name)); - ED_armature_bone_rename( - bmain, reinterpret_cast<bArmature *>(ob->data), oldname, newname); + ED_armature_bone_rename(bmain, static_cast<bArmature *>(ob->data), oldname, newname); WM_msg_publish_rna_prop(mbus, &arm->id, pchan->bone, Bone, name); WM_event_add_notifier(C, NC_OBJECT | ND_POSE, nullptr); break; } case TSE_POSEGRP: { Object *ob = (Object *)tselem->id; /* id = object. */ - bActionGroup *grp = reinterpret_cast<bActionGroup *>(te->directdata); + bActionGroup *grp = static_cast<bActionGroup *>(te->directdata); BLI_uniquename(&ob->pose->agroups, grp, @@ -823,7 +822,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) } case TSE_GP_LAYER: { bGPdata *gpd = (bGPdata *)tselem->id; /* id = GP Datablock */ - bGPDlayer *gpl = reinterpret_cast<bGPDlayer *>(te->directdata); + bGPDlayer *gpl = static_cast<bGPDlayer *>(te->directdata); /* always make layer active */ BKE_gpencil_layer_active_set(gpd, gpl); @@ -839,7 +838,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) } case TSE_R_LAYER: { Scene *scene = (Scene *)tselem->id; - ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata); + ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata); /* Restore old name. */ char newname[sizeof(view_layer->name)]; @@ -991,7 +990,7 @@ static bool outliner_restrict_properties_collection_set(Scene *scene, { TreeStoreElem *tselem = TREESTORE(te); LayerCollection *layer_collection = (tselem->type == TSE_LAYER_COLLECTION) ? - reinterpret_cast<LayerCollection *>(te->directdata) : + static_cast<LayerCollection *>(te->directdata) : nullptr; Collection *collection = outliner_collection_from_tree_element(te); @@ -1105,7 +1104,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) { if (space_outliner->show_restrict_flags & SO_RESTRICT_RENDER) { /* View layer render toggle. */ - ViewLayer *layer = reinterpret_cast<ViewLayer *>(te->directdata); + ViewLayer *layer = static_cast<ViewLayer *>(te->directdata); bt = uiDefIconButBitS(block, UI_BTYPE_ICON_TOGGLE_N, @@ -1329,7 +1328,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, bPoseChannel *pchan = (bPoseChannel *)te->directdata; Bone *bone = pchan->bone; Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); RNA_pointer_create(&arm->id, &RNA_Bone, bone, &ptr); @@ -1479,8 +1478,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, scene, te, &collection_ptr, &layer_collection_ptr, &props, &props_active)) { LayerCollection *layer_collection = (tselem->type == TSE_LAYER_COLLECTION) ? - reinterpret_cast<LayerCollection *>( - te->directdata) : + static_cast<LayerCollection *>(te->directdata) : nullptr; Collection *collection = outliner_collection_from_tree_element(te); @@ -2518,7 +2516,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.drag_id = tselem->id; break; case TSE_CONSTRAINT: { - bConstraint *con = reinterpret_cast<bConstraint *>(te->directdata); + bConstraint *con = static_cast<bConstraint *>(te->directdata); data.drag_id = tselem->id; switch ((eBConstraint_Types)con->type) { case CONSTRAINT_TYPE_CAMERASOLVER: @@ -2635,9 +2633,8 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.drag_id = tselem->id; if (ob->type != OB_GPENCIL) { - ModifierData *md = reinterpret_cast<ModifierData *>( - BLI_findlink(&ob->modifiers, tselem->nr)); - const ModifierTypeInfo *modifier_type = reinterpret_cast<const ModifierTypeInfo *>( + ModifierData *md = static_cast<ModifierData *>(BLI_findlink(&ob->modifiers, tselem->nr)); + const ModifierTypeInfo *modifier_type = static_cast<const ModifierTypeInfo *>( BKE_modifier_get_info((ModifierType)md->type)); if (modifier_type != nullptr) { data.icon = modifier_type->icon; @@ -2648,7 +2645,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) } else { /* grease pencil modifiers */ - GpencilModifierData *md = reinterpret_cast<GpencilModifierData *>( + GpencilModifierData *md = static_cast<GpencilModifierData *>( BLI_findlink(&ob->greasepencil_modifiers, tselem->nr)); switch ((GpencilModifierType)md->type) { case eGpencilModifierType_Noise: @@ -2807,7 +2804,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) const PointerRNA &ptr = te_rna_struct->getPointerRNA(); if (RNA_struct_is_ID(ptr.type)) { - data.drag_id = reinterpret_cast<ID *>(ptr.data); + data.drag_id = static_cast<ID *>(ptr.data); data.icon = RNA_struct_ui_icon(ptr.type); } else { @@ -2858,7 +2855,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) } /** - * \return Return true if the element has an icon that was drawn, false if it doesn't have an icon. + * \return true if the element has an icon that was drawn, false if it doesn't have an icon. */ static bool tselem_draw_icon(uiBlock *block, int xmax, diff --git a/source/blender/editors/space_outliner/outliner_edit.cc b/source/blender/editors/space_outliner/outliner_edit.cc index 16da4f7b1dd..f22db5d20fc 100644 --- a/source/blender/editors/space_outliner/outliner_edit.cc +++ b/source/blender/editors/space_outliner/outliner_edit.cc @@ -598,9 +598,9 @@ static int outliner_id_remap_exec(bContext *C, wmOperator *op) SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); const short id_type = (short)RNA_enum_get(op->ptr, "id_type"); - ID *old_id = reinterpret_cast<ID *>( + ID *old_id = static_cast<ID *>( BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "old_id"))); - ID *new_id = reinterpret_cast<ID *>( + ID *new_id = static_cast<ID *>( BLI_findlink(which_libbase(CTX_data_main(C), id_type), RNA_enum_get(op->ptr, "new_id"))); /* check for invalid states */ @@ -694,9 +694,9 @@ static const EnumPropertyItem *outliner_id_itemf(bContext *C, int i = 0; short id_type = (short)RNA_enum_get(ptr, "id_type"); - ID *id = reinterpret_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first); + ID *id = static_cast<ID *>(which_libbase(CTX_data_main(C), id_type)->first); - for (; id; id = reinterpret_cast<ID *>(id->next)) { + for (; id; id = static_cast<ID *>(id->next)) { item_tmp.identifier = item_tmp.name = id->name + 2; item_tmp.value = i++; RNA_enum_item_add(&item, &totitem, &item_tmp); @@ -1818,7 +1818,7 @@ static void tree_element_to_path(TreeElement *te, /* ptr->data not ptr->owner_id seems to be the one we want, * since ptr->data is sometimes the owner of this ID? */ if (RNA_struct_is_ID(ptr.type)) { - *id = reinterpret_cast<ID *>(ptr.data); + *id = static_cast<ID *>(ptr.data); /* clear path */ if (*path) { @@ -2053,8 +2053,7 @@ static KeyingSet *verify_active_keyingset(Scene *scene, short add) /* try to find one from scene */ if (scene->active_keyingset > 0) { - ks = reinterpret_cast<KeyingSet *>( - BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1)); + ks = static_cast<KeyingSet *>(BLI_findlink(&scene->keyingsets, scene->active_keyingset - 1)); } /* Add if none found */ diff --git a/source/blender/editors/space_outliner/outliner_select.cc b/source/blender/editors/space_outliner/outliner_select.cc index 877e0fc325c..31ae4aef7ff 100644 --- a/source/blender/editors/space_outliner/outliner_select.cc +++ b/source/blender/editors/space_outliner/outliner_select.cc @@ -220,7 +220,7 @@ static void tree_element_viewlayer_activate(bContext *C, TreeElement *te) return; } - ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata); + ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata); wmWindow *win = CTX_wm_window(C); Scene *scene = WM_window_get_active_scene(win); @@ -239,7 +239,7 @@ static void do_outliner_object_select_recursive(ViewLayer *view_layer, { Base *base; - for (base = reinterpret_cast<Base *>(FIRSTBASE(view_layer)); base; base = base->next) { + for (base = static_cast<Base *>(FIRSTBASE(view_layer)); base; base = base->next) { Object *ob = base->object; if ((((base->flag & BASE_VISIBLE_DEPSGRAPH) != 0) && BKE_object_is_child_recursive(ob_parent, ob))) { @@ -418,7 +418,7 @@ static void tree_element_camera_activate(bContext *C, Scene *scene, TreeElement scene->camera = ob; Main *bmain = CTX_data_main(C); - wmWindowManager *wm = reinterpret_cast<wmWindowManager *>(bmain->wm.first); + wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); WM_windows_scene_data_sync(&wm->windows, scene); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); @@ -458,7 +458,7 @@ static void tree_element_defgroup_activate(bContext *C, TreeElement *te, TreeSto static void tree_element_gplayer_activate(bContext *C, TreeElement *te, TreeStoreElem *tselem) { bGPdata *gpd = (bGPdata *)tselem->id; - bGPDlayer *gpl = reinterpret_cast<bGPDlayer *>(te->directdata); + bGPDlayer *gpl = static_cast<bGPDlayer *>(te->directdata); /* We can only have a single "active" layer at a time * and there must always be an active layer... */ @@ -486,8 +486,8 @@ static void tree_element_posechannel_activate(bContext *C, bool recursive) { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); - bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata); + bArmature *arm = static_cast<bArmature *>(ob->data); + bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); if (!(pchan->bone->flag & BONE_HIDDEN_P)) { if (set != OL_SETSEL_EXTEND) { @@ -508,7 +508,7 @@ static void tree_element_posechannel_activate(bContext *C, } if (ob != ob_iter) { - DEG_id_tag_update(reinterpret_cast<ID *>(ob_iter->data), ID_RECALC_SELECT); + DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT); } } MEM_freeN(objects); @@ -541,14 +541,14 @@ static void tree_element_bone_activate(bContext *C, bool recursive) { bArmature *arm = (bArmature *)tselem->id; - Bone *bone = reinterpret_cast<Bone *>(te->directdata); + Bone *bone = static_cast<Bone *>(te->directdata); if (!(bone->flag & BONE_HIDDEN_P)) { Object *ob = OBACT(view_layer); if (ob) { if (set != OL_SETSEL_EXTEND) { /* single select forces all other bones to get unselected */ - for (Bone *bone_iter = reinterpret_cast<Bone *>(arm->bonebase.first); bone_iter != nullptr; + for (Bone *bone_iter = static_cast<Bone *>(arm->bonebase.first); bone_iter != nullptr; bone_iter = bone_iter->next) { bone_iter->flag &= ~(BONE_TIPSEL | BONE_SELECTED | BONE_ROOTSEL); do_outliner_bone_select_recursive(arm, bone_iter, false); @@ -590,7 +590,7 @@ static void tree_element_ebone_activate(bContext *C, bool recursive) { bArmature *arm = (bArmature *)tselem->id; - EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata); + EditBone *ebone = static_cast<EditBone *>(te->directdata); if (set == OL_SETSEL_NORMAL) { if (!(ebone->flag & BONE_HIDDEN_A)) { @@ -703,7 +703,7 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED #if 0 select_single_seq(seq, 1); #endif - Sequence *p = reinterpret_cast<Sequence *>(ed->seqbasep->first); + Sequence *p = static_cast<Sequence *>(ed->seqbasep->first); while (p) { if ((!p->strip) || (!p->strip->stripdata) || (p->strip->stripdata->name[0] == '\0')) { p = p->next; @@ -722,7 +722,7 @@ static void tree_element_sequence_dup_activate(Scene *scene, TreeElement *UNUSED static void tree_element_master_collection_activate(const bContext *C) { ViewLayer *view_layer = CTX_data_view_layer(C); - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>( + LayerCollection *layer_collection = static_cast<LayerCollection *>( view_layer->layer_collections.first); BKE_layer_collection_activate(view_layer, layer_collection); /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work @@ -733,7 +733,7 @@ static void tree_element_master_collection_activate(const bContext *C) static void tree_element_layer_collection_activate(bContext *C, TreeElement *te) { Scene *scene = CTX_data_scene(C); - LayerCollection *layer_collection = reinterpret_cast<LayerCollection *>(te->directdata); + LayerCollection *layer_collection = static_cast<LayerCollection *>(te->directdata); ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection); BKE_layer_collection_activate(view_layer, layer_collection); /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work @@ -857,7 +857,7 @@ static eOLDrawState tree_element_bone_state_get(const ViewLayer *view_layer, const TreeStoreElem *tselem) { const bArmature *arm = (const bArmature *)tselem->id; - const Bone *bone = reinterpret_cast<Bone *>(te->directdata); + const Bone *bone = static_cast<Bone *>(te->directdata); const Object *ob = OBACT(view_layer); if (ob && ob->data == arm) { if (bone->flag & BONE_SELECTED) { @@ -869,7 +869,7 @@ static eOLDrawState tree_element_bone_state_get(const ViewLayer *view_layer, static eOLDrawState tree_element_ebone_state_get(const TreeElement *te) { - const EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata); + const EditBone *ebone = static_cast<EditBone *>(te->directdata); if (ebone->flag & BONE_SELECTED) { return OL_DRAWSEL_NORMAL; } @@ -913,7 +913,7 @@ static eOLDrawState tree_element_posechannel_state_get(const Object *ob_pose, const TreeStoreElem *tselem) { const Object *ob = (const Object *)tselem->id; - const bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata); + const bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); if (ob == ob_pose && ob->pose) { if (pchan->bone->flag & BONE_SELECTED) { return OL_DRAWSEL_NORMAL; @@ -929,7 +929,7 @@ static eOLDrawState tree_element_viewlayer_state_get(const bContext *C, const Tr return OL_DRAWSEL_NONE; } - const ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata); + const ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata); if (CTX_data_view_layer(C) == view_layer) { return OL_DRAWSEL_NORMAL; @@ -1229,7 +1229,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE /* Expand the selected constraint in the properties editor. */ if (tselem->type != TSE_CONSTRAINT_BASE) { - BKE_constraint_panel_expand(reinterpret_cast<bConstraint *>(te->directdata)); + BKE_constraint_panel_expand(static_cast<bConstraint *>(te->directdata)); } break; } @@ -1242,8 +1242,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE Object *ob = (Object *)tselem->id; if (ob->type == OB_GPENCIL) { - BKE_gpencil_modifier_panel_expand( - reinterpret_cast<GpencilModifierData *>(te->directdata)); + BKE_gpencil_modifier_panel_expand(static_cast<GpencilModifierData *>(te->directdata)); } else { ModifierData *md = (ModifierData *)te->directdata; @@ -1276,12 +1275,12 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE context = BCONTEXT_SHADERFX; if (tselem->type != TSE_GPENCIL_EFFECT_BASE) { - BKE_shaderfx_panel_expand(reinterpret_cast<ShaderFxData *>(te->directdata)); + BKE_shaderfx_panel_expand(static_cast<ShaderFxData *>(te->directdata)); } break; case TSE_BONE: { bArmature *arm = (bArmature *)tselem->id; - Bone *bone = reinterpret_cast<Bone *>(te->directdata); + Bone *bone = static_cast<Bone *>(te->directdata); RNA_pointer_create(&arm->id, &RNA_Bone, bone, &ptr); context = BCONTEXT_BONE; @@ -1289,7 +1288,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE } case TSE_EBONE: { bArmature *arm = (bArmature *)tselem->id; - EditBone *ebone = reinterpret_cast<EditBone *>(te->directdata); + EditBone *ebone = static_cast<EditBone *>(te->directdata); RNA_pointer_create(&arm->id, &RNA_EditBone, ebone, &ptr); context = BCONTEXT_BONE; @@ -1297,8 +1296,8 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE } case TSE_POSE_CHANNEL: { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); - bPoseChannel *pchan = reinterpret_cast<bPoseChannel *>(te->directdata); + bArmature *arm = static_cast<bArmature *>(ob->data); + bPoseChannel *pchan = static_cast<bPoseChannel *>(te->directdata); RNA_pointer_create(&arm->id, &RNA_PoseBone, pchan, &ptr); context = BCONTEXT_BONE; @@ -1306,7 +1305,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE } case TSE_POSE_BASE: { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr); context = BCONTEXT_DATA; @@ -1314,7 +1313,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE } case TSE_R_LAYER_BASE: case TSE_R_LAYER: { - ViewLayer *view_layer = reinterpret_cast<ViewLayer *>(te->directdata); + ViewLayer *view_layer = static_cast<ViewLayer *>(te->directdata); RNA_pointer_create(tselem->id, &RNA_ViewLayer, view_layer, &ptr); context = BCONTEXT_VIEW_LAYER; @@ -1323,7 +1322,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE case TSE_POSEGRP_BASE: case TSE_POSEGRP: { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); RNA_pointer_create(&arm->id, &RNA_Armature, arm, &ptr); context = BCONTEXT_DATA; @@ -1823,7 +1822,7 @@ static TreeElement *outliner_find_rightmost_visible_child(SpaceOutliner *space_o { while (te->subtree.last) { if (TSELEM_OPEN(TREESTORE(te), space_outliner)) { - te = reinterpret_cast<TreeElement *>(te->subtree.last); + te = static_cast<TreeElement *>(te->subtree.last); } else { break; @@ -1867,7 +1866,7 @@ static TreeElement *outliner_find_next_element(SpaceOutliner *space_outliner, Tr TreeStoreElem *tselem = TREESTORE(te); if (TSELEM_OPEN(tselem, space_outliner) && te->subtree.first) { - te = reinterpret_cast<TreeElement *>(te->subtree.first); + te = static_cast<TreeElement *>(te->subtree.first); } else if (te->next) { te = te->next; @@ -1904,7 +1903,7 @@ static TreeElement *outliner_walk_right(SpaceOutliner *space_outliner, /* Only walk down a level if the element is open and not toggling expand */ if (!toggle_all && TSELEM_OPEN(tselem, space_outliner) && !BLI_listbase_is_empty(&te->subtree)) { - te = reinterpret_cast<TreeElement *>(te->subtree.first); + te = static_cast<TreeElement *>(te->subtree.first); } else { outliner_item_openclose(space_outliner, te, true, toggle_all); @@ -1955,7 +1954,7 @@ static TreeElement *find_walk_select_start_element(SpaceOutliner *space_outliner /* If no active element exists, use the first element in the tree */ if (!active_te) { - active_te = reinterpret_cast<TreeElement *>(space_outliner->tree.first); + active_te = static_cast<TreeElement *>(space_outliner->tree.first); *changed = true; } diff --git a/source/blender/editors/space_outliner/outliner_sync.cc b/source/blender/editors/space_outliner/outliner_sync.cc index 36bc7c31b91..772a5826f9f 100644 --- a/source/blender/editors/space_outliner/outliner_sync.cc +++ b/source/blender/editors/space_outliner/outliner_sync.cc @@ -77,8 +77,8 @@ void ED_outliner_select_sync_flag_outliners(const bContext *C) Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); - for (bScreen *screen = reinterpret_cast<bScreen *>(bmain->screens.first); screen; - screen = reinterpret_cast<bScreen *>(screen->id.next)) { + for (bScreen *screen = static_cast<bScreen *>(bmain->screens.first); screen; + screen = static_cast<bScreen *>(screen->id.next)) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { if (sl->spacetype == SPACE_OUTLINER) { @@ -259,7 +259,7 @@ static void outliner_select_sync_to_pose_bone(TreeElement *te, GSet *selected_pbones) { Object *ob = (Object *)tselem->id; - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); bPoseChannel *pchan = (bPoseChannel *)te->directdata; short bone_flag = pchan->bone->flag; diff --git a/source/blender/editors/space_outliner/outliner_tools.cc b/source/blender/editors/space_outliner/outliner_tools.cc index ec80eacf6ab..f51a70af3bc 100644 --- a/source/blender/editors/space_outliner/outliner_tools.cc +++ b/source/blender/editors/space_outliner/outliner_tools.cc @@ -963,7 +963,7 @@ static void id_override_library_create_hierarchy_pre_process_fn(bContext *C, { BLI_assert(TSE_IS_REAL_ID(tselem)); - OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); + OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data); const bool do_hierarchy = data->do_hierarchy; ID *id_root_reference = tselem->id; @@ -1271,7 +1271,7 @@ static void id_override_library_reset_fn(bContext *C, { BLI_assert(TSE_IS_REAL_ID(tselem)); ID *id_root = tselem->id; - OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); + OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data); const bool do_hierarchy = data->do_hierarchy; if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { @@ -1302,7 +1302,7 @@ static void id_override_library_resync_fn(bContext *UNUSED(C), { BLI_assert(TSE_IS_REAL_ID(tselem)); ID *id_root = tselem->id; - OutlinerLibOverrideData *data = reinterpret_cast<OutlinerLibOverrideData *>(user_data); + OutlinerLibOverrideData *data = static_cast<OutlinerLibOverrideData *>(user_data); if (!ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { CLOG_WARN(&LOG, "Could not resync library override of data block '%s'", id_root->name); @@ -2002,7 +2002,7 @@ static void data_select_linked_fn(int event, const PointerRNA &ptr = te_rna_struct->getPointerRNA(); if (RNA_struct_is_ID(ptr.type)) { bContext *C = (bContext *)C_v; - ID *id = reinterpret_cast<ID *>(ptr.data); + ID *id = static_cast<ID *>(ptr.data); ED_object_select_linked_by_id(C, id); } @@ -2011,7 +2011,7 @@ static void data_select_linked_fn(int event, static void constraint_fn(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem), void *C_v) { - bContext *C = reinterpret_cast<bContext *>(C_v); + bContext *C = static_cast<bContext *>(C_v); Main *bmain = CTX_data_main(C); bConstraint *constraint = (bConstraint *)te->directdata; Object *ob = (Object *)outliner_search_back(te, ID_OB); @@ -2102,7 +2102,7 @@ static Base *outliner_batch_delete_hierarchy( } object = base->object; - for (child_base = reinterpret_cast<Base *>(view_layer->object_bases.first); child_base; + for (child_base = static_cast<Base *>(view_layer->object_bases.first); child_base; child_base = base_next) { base_next = child_base->next; for (parent = child_base->object->parent; parent && (parent != object); @@ -2325,7 +2325,7 @@ static void outliner_do_object_delete(bContext *C, static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void *customdata) { - ObjectEditData *data = reinterpret_cast<ObjectEditData *>(customdata); + ObjectEditData *data = static_cast<ObjectEditData *>(customdata); GSet *objects_to_delete = data->objects_set; TreeStoreElem *tselem = TREESTORE(te); @@ -2898,8 +2898,7 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); /* get action to use */ - act = reinterpret_cast<bAction *>( - BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action"))); + act = static_cast<bAction *>(BLI_findlink(&bmain->actions, RNA_enum_get(op->ptr, "action"))); if (act == nullptr) { BKE_report(op->reports, RPT_ERROR, "No valid action to add"); diff --git a/source/blender/editors/space_outliner/outliner_tree.cc b/source/blender/editors/space_outliner/outliner_tree.cc index 3357a456e30..0906bbb5797 100644 --- a/source/blender/editors/space_outliner/outliner_tree.cc +++ b/source/blender/editors/space_outliner/outliner_tree.cc @@ -90,7 +90,7 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner) BLI_mempool_iter iter; BLI_mempool_iternew(ts, &iter); - while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { + while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { tselem->used = 0; } @@ -100,7 +100,7 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner) space_outliner->storeflag &= ~SO_TREESTORE_CLEANUP; BLI_mempool_iternew(ts, &iter); - while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { + while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { if (tselem->id == nullptr) { unused++; } @@ -120,9 +120,9 @@ static void outliner_storage_cleanup(SpaceOutliner *space_outliner) BLI_mempool *new_ts = BLI_mempool_create( sizeof(TreeStoreElem), BLI_mempool_len(ts) - unused, 512, BLI_MEMPOOL_ALLOW_ITER); BLI_mempool_iternew(ts, &iter); - while ((tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { + while ((tselem = static_cast<TreeStoreElem *>(BLI_mempool_iterstep(&iter)))) { if (tselem->id) { - tsenew = reinterpret_cast<TreeStoreElem *>(BLI_mempool_alloc(new_ts)); + tsenew = static_cast<TreeStoreElem *>(BLI_mempool_alloc(new_ts)); *tsenew = *tselem; } } @@ -151,7 +151,7 @@ static void check_persistent( sizeof(TreeStoreElem), 1, 512, BLI_MEMPOOL_ALLOW_ITER); } if (space_outliner->runtime->treehash == nullptr) { - space_outliner->runtime->treehash = reinterpret_cast<GHash *>( + space_outliner->runtime->treehash = static_cast<GHash *>( BKE_outliner_treehash_create_from_treestore(space_outliner->treestore)); } @@ -166,7 +166,7 @@ static void check_persistent( } /* add 1 element to treestore */ - tselem = reinterpret_cast<TreeStoreElem *>(BLI_mempool_alloc(space_outliner->treestore)); + tselem = static_cast<TreeStoreElem *>(BLI_mempool_alloc(space_outliner->treestore)); tselem->type = type; tselem->nr = type ? nr : 0; tselem->id = id; @@ -293,7 +293,7 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, outliner_add_element(space_outliner, &te->subtree, ob->data, te, TSE_SOME_ID, 0); if (ob->pose) { - bArmature *arm = reinterpret_cast<bArmature *>(ob->data); + bArmature *arm = static_cast<bArmature *>(ob->data); TreeElement *tenla = outliner_add_element( space_outliner, &te->subtree, ob, te, TSE_POSE_BASE, 0); tenla->name = IFACE_("Pose"); @@ -339,7 +339,7 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, } } /* make hierarchy */ - TreeElement *ten = reinterpret_cast<TreeElement *>(tenla->subtree.first); + TreeElement *ten = static_cast<TreeElement *>(tenla->subtree.first); while (ten) { TreeElement *nten = ten->next, *par; tselem = TREESTORE(ten); @@ -694,15 +694,15 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, ebone->temp.p = ten; } /* make hierarchy */ - TreeElement *ten = arm->edbo->first ? reinterpret_cast<TreeElement *>( - ((EditBone *)arm->edbo->first)->temp.p) : - nullptr; + TreeElement *ten = arm->edbo->first ? + static_cast<TreeElement *>(((EditBone *)arm->edbo->first)->temp.p) : + nullptr; while (ten) { TreeElement *nten = ten->next, *par; EditBone *ebone = (EditBone *)ten->directdata; if (ebone->parent) { BLI_remlink(&te->subtree, ten); - par = reinterpret_cast<TreeElement *>(ebone->parent->temp.p); + par = static_cast<TreeElement *>(ebone->parent->temp.p); BLI_addtail(&par->subtree, ten); ten->parent = par; } @@ -805,12 +805,12 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, short index, const bool expand) { - ID *id = reinterpret_cast<ID *>(idv); + ID *id = static_cast<ID *>(idv); if (ELEM(type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_RNA_ARRAY_ELEM)) { id = ((PointerRNA *)idv)->owner_id; if (!id) { - id = reinterpret_cast<ID *>(((PointerRNA *)idv)->data); + id = static_cast<ID *>(((PointerRNA *)idv)->data); } } else if (type == TSE_GP_LAYER) { @@ -990,8 +990,8 @@ struct tTreeSort { /* alphabetical comparator, trying to put objects first */ static int treesort_alpha_ob(const void *v1, const void *v2) { - const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1); - const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2); + const tTreeSort *x1 = static_cast<const tTreeSort *>(v1); + const tTreeSort *x2 = static_cast<const tTreeSort *>(v2); /* first put objects last (hierarchy) */ int comp = (x1->idcode == ID_OB); @@ -1029,8 +1029,8 @@ static int treesort_alpha_ob(const void *v1, const void *v2) /* Move children that are not in the collection to the end of the list. */ static int treesort_child_not_in_collection(const void *v1, const void *v2) { - const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1); - const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2); + const tTreeSort *x1 = static_cast<const tTreeSort *>(v1); + const tTreeSort *x2 = static_cast<const tTreeSort *>(v2); /* Among objects first come the ones in the collection, followed by the ones not on it. * This way we can have the dashed lines in a separate style connecting the former. */ @@ -1043,8 +1043,8 @@ static int treesort_child_not_in_collection(const void *v1, const void *v2) /* alphabetical comparator */ static int treesort_alpha(const void *v1, const void *v2) { - const tTreeSort *x1 = reinterpret_cast<const tTreeSort *>(v1); - const tTreeSort *x2 = reinterpret_cast<const tTreeSort *>(v2); + const tTreeSort *x1 = static_cast<const tTreeSort *>(v1); + const tTreeSort *x2 = static_cast<const tTreeSort *>(v2); int comp = BLI_strcasecmp_natural(x1->name, x2->name); @@ -1101,7 +1101,7 @@ static int treesort_obtype_alpha(const void *v1, const void *v2) /* sort happens on each subtree individual */ static void outliner_sort(ListBase *lb) { - TreeElement *last_te = reinterpret_cast<TreeElement *>(lb->last); + TreeElement *last_te = static_cast<TreeElement *>(lb->last); if (last_te == nullptr) { return; } @@ -1113,7 +1113,7 @@ static void outliner_sort(ListBase *lb) int totelem = BLI_listbase_count(lb); if (totelem > 1) { - tTreeSort *tear = reinterpret_cast<tTreeSort *>( + tTreeSort *tear = static_cast<tTreeSort *>( MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array")); tTreeSort *tp = tear; int skip = 0; @@ -1169,7 +1169,7 @@ static void outliner_sort(ListBase *lb) static void outliner_collections_children_sort(ListBase *lb) { - TreeElement *last_te = reinterpret_cast<TreeElement *>(lb->last); + TreeElement *last_te = static_cast<TreeElement *>(lb->last); if (last_te == nullptr) { return; } @@ -1180,7 +1180,7 @@ static void outliner_collections_children_sort(ListBase *lb) int totelem = BLI_listbase_count(lb); if (totelem > 1) { - tTreeSort *tear = reinterpret_cast<tTreeSort *>( + tTreeSort *tear = static_cast<tTreeSort *>( MEM_mallocN(totelem * sizeof(tTreeSort), "tree sort array")); tTreeSort *tp = tear; @@ -1551,8 +1551,7 @@ static TreeElement *outliner_extract_children_from_subtree(TreeElement *element, if (outliner_element_is_collection_or_object(element)) { TreeElement *te_prev = nullptr; - for (TreeElement *te = reinterpret_cast<TreeElement *>(element->subtree.last); te; - te = te_prev) { + for (TreeElement *te = static_cast<TreeElement *>(element->subtree.last); te; te = te_prev) { te_prev = te->prev; if (!outliner_element_is_collection_or_object(te)) { @@ -1579,7 +1578,7 @@ static int outliner_filter_subtree(SpaceOutliner *space_outliner, TreeElement *te, *te_next; TreeStoreElem *tselem; - for (te = reinterpret_cast<TreeElement *>(lb->first); te; te = te_next) { + for (te = static_cast<TreeElement *>(lb->first); te; te = te_next) { te_next = te->next; if ((outliner_element_visible_get(view_layer, te, exclude_filter) == false)) { /* Don't free the tree, but extract the children from the parent and add to this tree. */ diff --git a/source/blender/editors/space_outliner/outliner_utils.cc b/source/blender/editors/space_outliner/outliner_utils.cc index 0db612ce6db..d8c50cd04f9 100644 --- a/source/blender/editors/space_outliner/outliner_utils.cc +++ b/source/blender/editors/space_outliner/outliner_utils.cc @@ -98,7 +98,7 @@ static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement * float view_co_x, bool *r_is_merged_icon) { - TreeElement *child_te = reinterpret_cast<TreeElement *>(parent_te->subtree.first); + TreeElement *child_te = static_cast<TreeElement *>(parent_te->subtree.first); while (child_te) { const bool over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); @@ -282,8 +282,7 @@ bool outliner_tree_traverse(const SpaceOutliner *space_outliner, TreeTraversalFunc func, void *customdata) { - for (TreeElement *te = reinterpret_cast<TreeElement *>(tree->first), *te_next; te; - te = te_next) { + for (TreeElement *te = static_cast<TreeElement *>(tree->first), *te_next; te; te = te_next) { TreeTraversalAction func_retval = TRAVERSE_CONTINUE; /* in case te is freed in callback */ TreeStoreElem *tselem = TREESTORE(te); diff --git a/source/blender/editors/space_outliner/space_outliner.cc b/source/blender/editors/space_outliner/space_outliner.cc index 5bcd1edebc0..61bc3d35dfd 100644 --- a/source/blender/editors/space_outliner/space_outliner.cc +++ b/source/blender/editors/space_outliner/space_outliner.cc @@ -101,7 +101,7 @@ static void outliner_main_region_listener(const wmRegionListenerParams *params) ScrArea *area = params->area; ARegion *region = params->region; wmNotifier *wmn = params->notifier; - SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); + SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first); /* context changes */ switch (wmn->category) { @@ -264,7 +264,7 @@ static void outliner_main_region_message_subscribe(const wmRegionMessageSubscrib struct wmMsgBus *mbus = params->message_bus; ScrArea *area = params->area; ARegion *region = params->region; - SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); + SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first); wmMsgSubscribeValue msg_sub_value_region_tag_redraw{}; msg_sub_value_region_tag_redraw.owner = region; @@ -361,7 +361,7 @@ static void outliner_free(SpaceLink *sl) /* spacetype; init callback */ static void outliner_init(wmWindowManager *UNUSED(wm), ScrArea *area) { - SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); + SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first); if (space_outliner->runtime == nullptr) { space_outliner->runtime = MEM_new<SpaceOutliner_Runtime>("SpaceOutliner_Runtime"); @@ -437,7 +437,7 @@ static void outliner_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRe static void outliner_deactivate(struct ScrArea *area) { /* Remove hover highlights */ - SpaceOutliner *space_outliner = reinterpret_cast<SpaceOutliner *>(area->spacedata.first); + SpaceOutliner *space_outliner = static_cast<SpaceOutliner *>(area->spacedata.first); outliner_flag_set(*space_outliner, TSE_HIGHLIGHTED_ANY, false); ED_region_tag_redraw_no_rebuild(BKE_area_find_region_type(area, RGN_TYPE_WINDOW)); } diff --git a/source/blender/editors/space_outliner/tree/common.cc b/source/blender/editors/space_outliner/tree/common.cc index 349d36e2fe6..e590b0c97d1 100644 --- a/source/blender/editors/space_outliner/tree/common.cc +++ b/source/blender/editors/space_outliner/tree/common.cc @@ -38,7 +38,7 @@ void outliner_make_object_parent_hierarchy(ListBase *lb) { /* build hierarchy */ /* XXX also, set extents here... */ - TreeElement *te = reinterpret_cast<TreeElement *>(lb->first); + TreeElement *te = static_cast<TreeElement *>(lb->first); while (te) { TreeElement *ten = te->next; TreeStoreElem *tselem = TREESTORE(te); diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index a401662297a..4a540c3ce87 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -4,6 +4,9 @@ * \ingroup spoutliner */ +#include <string> +#include <string_view> + #include "DNA_anim_types.h" #include "DNA_listBase.h" #include "DNA_space_types.h" @@ -57,7 +60,7 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i return std::make_unique<TreeElementLabel>(legacy_te, static_cast<const char *>(idv)); case TSE_ANIM_DATA: return std::make_unique<TreeElementAnimData>(legacy_te, - *reinterpret_cast<IdAdtTemplate *>(idv)->adt); + *static_cast<IdAdtTemplate *>(idv)->adt); case TSE_DRIVER_BASE: return std::make_unique<TreeElementDriverBase>(legacy_te, *static_cast<AnimData *>(idv)); case TSE_NLA: @@ -83,22 +86,20 @@ std::unique_ptr<AbstractTreeElement> AbstractTreeElement::createFromType(const i return std::make_unique<TreeElementOverridesPropertyOperation>( legacy_te, *static_cast<TreeElementOverridesData *>(idv)); case TSE_RNA_STRUCT: - return std::make_unique<TreeElementRNAStruct>(legacy_te, - *reinterpret_cast<PointerRNA *>(idv)); + return std::make_unique<TreeElementRNAStruct>(legacy_te, *static_cast<PointerRNA *>(idv)); case TSE_RNA_PROPERTY: return std::make_unique<TreeElementRNAProperty>( - legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index); + legacy_te, *static_cast<PointerRNA *>(idv), legacy_te.index); case TSE_RNA_ARRAY_ELEM: return std::make_unique<TreeElementRNAArrayElement>( - legacy_te, *reinterpret_cast<PointerRNA *>(idv), legacy_te.index); + legacy_te, *static_cast<PointerRNA *>(idv), legacy_te.index); case TSE_SEQUENCE: - return std::make_unique<TreeElementSequence>(legacy_te, *reinterpret_cast<Sequence *>(idv)); + return std::make_unique<TreeElementSequence>(legacy_te, *static_cast<Sequence *>(idv)); case TSE_SEQ_STRIP: - return std::make_unique<TreeElementSequenceStrip>(legacy_te, - *reinterpret_cast<Strip *>(idv)); + return std::make_unique<TreeElementSequenceStrip>(legacy_te, *static_cast<Strip *>(idv)); case TSE_SEQUENCE_DUP: - return std::make_unique<TreeElementSequenceStripDuplicate>( - legacy_te, *reinterpret_cast<Sequence *>(idv)); + return std::make_unique<TreeElementSequenceStripDuplicate>(legacy_te, + *static_cast<Sequence *>(idv)); default: break; } @@ -116,6 +117,17 @@ std::optional<BIFIconID> AbstractTreeElement::getIcon() const return {}; } +void AbstractTreeElement::print_path() +{ + std::string path = legacy_te_.name; + + for (TreeElement *parent = legacy_te_.parent; parent; parent = parent->parent) { + path = parent->name + std::string_view("/") + path; + } + + std::cout << path << std::endl; +} + void AbstractTreeElement::uncollapse_by_default(TreeElement *legacy_te) { if (!TREESTORE(legacy_te)->used) { diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index a3598e7740b..fc6211f20ea 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -75,6 +75,16 @@ class AbstractTreeElement { virtual std::optional<BIFIconID> getIcon() const; /** + * Debugging helper: Print effective path of this tree element, constructed out of the + * #TreeElement.name of each element. E.g.: + * - Lorem + * - ipsum dolor sit + * - amet + * will print: Lorem/ipsum dolor sit/amet. + */ + void print_path(); + + /** * Expand this tree element if it is displayed for the first time (as identified by its * tree-store element). * diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc index ab2555954d6..49cabd5117f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.cc @@ -153,7 +153,7 @@ void TreeElementOverridesBase::expand(SpaceOutliner &space_outliner) const /** \} */ /* -------------------------------------------------------------------- */ -/** \name Overriden Property +/** \name Overridden Property * * Represents an RNA property that was overridden. * @@ -187,7 +187,7 @@ StringRefNull TreeElementOverridesProperty::getWarning() const /** \} */ /* -------------------------------------------------------------------- */ -/** \name Overriden Property Operation +/** \name Overridden Property Operation * * See #TreeElementOverridesPropertyOperation. * \{ */ @@ -371,9 +371,7 @@ void OverrideRNAPathTreeBuilder::ensure_entire_collection( const char *coll_prop_path, short &index) { - AbstractTreeElement *abstract_parent = tree_element_cast<AbstractTreeElement>(&te_to_expand); - BLI_assert(abstract_parent != nullptr); - UNUSED_VARS_NDEBUG(abstract_parent); + BLI_assert(tree_element_cast<AbstractTreeElement>(&te_to_expand) != nullptr); TreeElement *previous_te = nullptr; int item_idx = 0; diff --git a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh index acf35033ce1..f8ca146a4ea 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_overrides.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_overrides.hh @@ -66,7 +66,7 @@ class TreeElementOverridesProperty : public AbstractTreeElement { }; /** - * Represent a single operation within an overriden property. While usually a single override + * Represent a single operation within an overridden property. While usually a single override * property represents a single operation (changing the value), a single overridden collection * property may have multiple operations, e.g. to insert or remove collection items. * diff --git a/source/blender/editors/space_outliner/tree/tree_element_rna.cc b/source/blender/editors/space_outliner/tree/tree_element_rna.cc index 914104f1f06..6dd5ec84041 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_rna.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_rna.cc @@ -117,7 +117,7 @@ void TreeElementRNAStruct::expand(SpaceOutliner &space_outliner) const for (int index = 0; index < tot; index++) { PointerRNA propptr; RNA_property_collection_lookup_int(&ptr, iterprop, index, &propptr); - if (!(RNA_property_flag(reinterpret_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) { + if (!(RNA_property_flag(static_cast<PropertyRNA *>(propptr.data)) & PROP_HIDDEN)) { outliner_add_element( &space_outliner, &legacy_te_.subtree, &ptr, &legacy_te_, TSE_RNA_PROPERTY, index); } @@ -146,7 +146,7 @@ TreeElementRNAProperty::TreeElementRNAProperty(TreeElement &legacy_te, PropertyRNA *iterprop = RNA_struct_iterator_property(rna_ptr.type); RNA_property_collection_lookup_int(&rna_ptr, iterprop, index, &propptr); - PropertyRNA *prop = reinterpret_cast<PropertyRNA *>(propptr.data); + PropertyRNA *prop = static_cast<PropertyRNA *>(propptr.data); legacy_te_.name = RNA_property_ui_name(prop); rna_prop_ = prop; @@ -232,8 +232,7 @@ TreeElementRNAArrayElement::TreeElementRNAArrayElement(TreeElement &legacy_te, char c = RNA_property_array_item_char(TreeElementRNAArrayElement::getPropertyRNA(), index); - legacy_te_.name = reinterpret_cast<char *>( - MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName")); + legacy_te_.name = static_cast<char *>(MEM_callocN(sizeof(char[20]), "OutlinerRNAArrayName")); if (c) { sprintf((char *)legacy_te_.name, " %c", c); } diff --git a/source/blender/editors/space_script/script_edit.c b/source/blender/editors/space_script/script_edit.c index e8c7590c1fe..a32c8a3f85a 100644 --- a/source/blender/editors/space_script/script_edit.c +++ b/source/blender/editors/space_script/script_edit.c @@ -100,7 +100,7 @@ static int script_reload_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* TODO(campbell): this crashes on netrender and keying sets, need to look into why + /* TODO(@campbellbarton): this crashes on netrender and keying sets, need to look into why * disable for now unless running in debug mode. */ /* It would be nice if we could detect when this is called from the Python diff --git a/source/blender/editors/space_sequencer/sequencer_drag_drop.c b/source/blender/editors/space_sequencer/sequencer_drag_drop.c index f6561cf07b9..4796d80b3a0 100644 --- a/source/blender/editors/space_sequencer/sequencer_drag_drop.c +++ b/source/blender/editors/space_sequencer/sequencer_drag_drop.c @@ -51,7 +51,9 @@ typedef struct SeqDropCoords { float start_frame, channel; int strip_len, channel_len; + float playback_rate; bool in_use; + bool has_read_mouse_pos; bool is_intersecting; bool use_snapping; float snap_point_x; @@ -63,7 +65,7 @@ typedef struct SeqDropCoords { * preloading data on drag start. * Therefore we will for now use a global variable for this. */ -static SeqDropCoords g_drop_coords = {.in_use = false}; +static SeqDropCoords g_drop_coords = {.in_use = false, .has_read_mouse_pos = false}; static void generic_poll_operations(const wmEvent *event, uint8_t type) { @@ -82,31 +84,134 @@ static bool image_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *ev } } - return WM_drag_is_ID_type(drag, ID_IM); + if (WM_drag_is_ID_type(drag, ID_IM)) { + generic_poll_operations(event, TH_SEQ_IMAGE); + return true; + } + + return false; } -static bool movie_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event) +static bool is_movie(wmDrag *drag) { if (drag->type == WM_DRAG_PATH) { - if (ELEM(drag->icon, 0, ICON_FILE_MOVIE, ICON_FILE_BLANK)) { /* Rule might not work? */ - generic_poll_operations(event, TH_SEQ_MOVIE); + if (ELEM(drag->icon, ICON_FILE_MOVIE, ICON_FILE_BLANK)) { /* Rule might not work? */ return true; } } + if (WM_drag_is_ID_type(drag, ID_MC)) { + return true; + } + return false; +} + +static bool movie_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event) +{ + if (is_movie(drag)) { + generic_poll_operations(event, TH_SEQ_MOVIE); + return true; + } - return WM_drag_is_ID_type(drag, ID_MC); + return false; } -static bool sound_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event) +static bool is_sound(wmDrag *drag) { if (drag->type == WM_DRAG_PATH) { if (ELEM(drag->icon, ICON_FILE_SOUND, ICON_FILE_BLANK)) { /* Rule might not work? */ - generic_poll_operations(event, TH_SEQ_AUDIO); return true; } } + if (WM_drag_is_ID_type(drag, ID_SO)) { + return true; + } + return false; +} - return WM_drag_is_ID_type(drag, ID_SO); +static bool sound_drop_poll(bContext *UNUSED(C), wmDrag *drag, const wmEvent *event) +{ + if (is_sound(drag)) { + generic_poll_operations(event, TH_SEQ_AUDIO); + return true; + } + + return false; +} + +static float update_overlay_strip_position_data(bContext *C, const int mval[2]) +{ + SeqDropCoords *coords = &g_drop_coords; + ARegion *region = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + int hand; + View2D *v2d = ®ion->v2d; + + /* Update the position were we would place the strip if we complete the drag and drop action. + */ + UI_view2d_region_to_view(v2d, mval[0], mval[1], &coords->start_frame, &coords->channel); + coords->start_frame = roundf(coords->start_frame); + if (coords->channel < 1.0f) { + coords->channel = 1; + } + + float start_frame = coords->start_frame; + float end_frame; + float strip_len; + + if (coords->playback_rate != 0.0f) { + float scene_playback_rate = (float)scene->r.frs_sec / scene->r.frs_sec_base; + strip_len = coords->strip_len / (coords->playback_rate / scene_playback_rate); + } + else { + strip_len = coords->strip_len; + } + + end_frame = coords->start_frame + strip_len; + + if (coords->use_snapping) { + /* Do snapping via the existing transform code. */ + int snap_delta; + float snap_frame; + bool valid_snap; + + valid_snap = ED_transform_snap_sequencer_to_closest_strip_calc( + scene, region, start_frame, end_frame, &snap_delta, &snap_frame); + + if (valid_snap) { + /* We snapped onto something! */ + start_frame += snap_delta; + coords->start_frame = start_frame; + end_frame = start_frame + strip_len; + coords->snap_point_x = snap_frame; + } + else { + /* Nothing was snapped to, disable snap drawing. */ + coords->use_snapping = false; + } + } + + if (strip_len < 1) { + /* Only check if there is a strip already under the mouse cursor. */ + coords->is_intersecting = find_nearest_seq(scene, ®ion->v2d, &hand, mval); + } + else { + /* Check if there is a strip that would intersect with the new strip(s). */ + coords->is_intersecting = false; + Sequence dummy_seq = {.machine = coords->channel, + .start = coords->start_frame, + .len = coords->strip_len, + .speed_factor = 1.0f, + .media_playback_rate = coords->playback_rate, + .flag = SEQ_AUTO_PLAYBACK_RATE}; + Editing *ed = SEQ_editing_ensure(scene); + + for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) { + coords->is_intersecting = SEQ_transform_test_overlap(scene, ed->seqbasep, &dummy_seq); + dummy_seq.machine++; + } + } + + return strip_len; } static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop) @@ -153,93 +258,77 @@ static void sequencer_drop_copy(bContext *C, wmDrag *drag, wmDropBox *drop) RNA_collection_add(drop->ptr, "files", &itemptr); RNA_string_set(&itemptr, "name", file); } - - if (g_drop_coords.in_use) { - RNA_int_set(drop->ptr, "frame_start", g_drop_coords.start_frame); - RNA_int_set(drop->ptr, "channel", g_drop_coords.channel); - RNA_boolean_set(drop->ptr, "overlap_shuffle_override", true); - } - else { - Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); - ListBase *seqbase = SEQ_active_seqbase_get(ed); - ListBase *channels = SEQ_channels_displayed_get(ed); - SpaceSeq *sseq = CTX_wm_space_seq(C); - - SeqCollection *strips = SEQ_query_rendered_strips( - scene, channels, seqbase, scene->r.cfra, sseq->chanshown); - - /* Get the top most strip channel that is in view.*/ - Sequence *seq; - int max_channel = -1; - SEQ_ITERATOR_FOREACH (seq, strips) { - max_channel = max_ii(seq->machine, max_channel); - } - - if (max_channel != -1) { - RNA_int_set(drop->ptr, "channel", max_channel); - } - SEQ_collection_free(strips); - } } -} -static void update_overlay_strip_poistion_data(bContext *C, const int mval[2]) -{ - SeqDropCoords *coords = &g_drop_coords; - ARegion *region = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - int hand; - View2D *v2d = ®ion->v2d; + if (g_drop_coords.in_use) { + if (!g_drop_coords.has_read_mouse_pos) { + /* We didn't read the mouse position, so we need to do it manually here. */ + int xy[2]; + wmWindow *win = CTX_wm_window(C); + xy[0] = win->eventstate->xy[0]; + xy[1] = win->eventstate->xy[1]; + + ARegion *region = CTX_wm_region(C); + int mval[2]; + /* Convert mouse coordinates to region local coordinates. */ + mval[0] = xy[0] - region->winrct.xmin; + mval[1] = xy[1] - region->winrct.ymin; + + update_overlay_strip_position_data(C, mval); + } - /* Update the position were we would place the strip if we complete the drag and drop action. - */ - UI_view2d_region_to_view(v2d, mval[0], mval[1], &coords->start_frame, &coords->channel); - coords->start_frame = roundf(coords->start_frame); - if (coords->channel < 1.0f) { - coords->channel = 1; + RNA_int_set(drop->ptr, "frame_start", g_drop_coords.start_frame); + RNA_int_set(drop->ptr, "channel", g_drop_coords.channel); + RNA_boolean_set(drop->ptr, "overlap_shuffle_override", true); } + else { + /* We are dropped inside the preview region. Put the strip on top of the + * current displayed frame. */ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + ListBase *channels = SEQ_channels_displayed_get(ed); + SpaceSeq *sseq = CTX_wm_space_seq(C); - float start_frame = coords->start_frame; - float end_frame = coords->start_frame + coords->strip_len; - - if (coords->use_snapping) { - /* Do snapping via the existing transform code. */ - int snap_delta; - float snap_frame; - bool valid_snap; - - valid_snap = ED_transform_snap_sequencer_to_closest_strip_calc( - scene, region, start_frame, end_frame, &snap_delta, &snap_frame); + SeqCollection *strips = SEQ_query_rendered_strips( + scene, channels, seqbase, scene->r.cfra, sseq->chanshown); - if (valid_snap) { - /* We snapped onto something! */ - start_frame += snap_delta; - coords->start_frame = start_frame; - end_frame = start_frame + coords->strip_len; - coords->snap_point_x = snap_frame; + /* Get the top most strip channel that is in view.*/ + Sequence *seq; + int max_channel = -1; + SEQ_ITERATOR_FOREACH (seq, strips) { + max_channel = max_ii(seq->machine, max_channel); } - else { - /* Nothing was snapped to, disable snap drawing. */ - coords->use_snapping = false; + + if (max_channel != -1) { + RNA_int_set(drop->ptr, "channel", max_channel); } + SEQ_collection_free(strips); } +} - if (coords->strip_len < 1) { - /* Only check if there is a strip already under the mouse cursor. */ - coords->is_intersecting = find_nearest_seq(scene, ®ion->v2d, &hand, mval); +static void get_drag_path(wmDrag *drag, char r_path[FILE_MAX]) +{ + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); + /* ID dropped. */ + if (id != NULL) { + const ID_Type id_type = GS(id->name); + if (id_type == ID_IM) { + Image *ima = (Image *)id; + BLI_strncpy(r_path, ima->filepath, FILE_MAX); + } + else if (id_type == ID_MC) { + MovieClip *clip = (MovieClip *)id; + BLI_strncpy(r_path, clip->filepath, FILE_MAX); + } + else if (id_type == ID_SO) { + bSound *sound = (bSound *)id; + BLI_strncpy(r_path, sound->filepath, FILE_MAX); + } + BLI_path_abs(r_path, BKE_main_blendfile_path_from_global()); } else { - /* Check if there is a strip that would intersect with the new strip(s). */ - coords->is_intersecting = false; - Sequence dummy_seq = { - .machine = coords->channel, .start = coords->start_frame, .len = coords->strip_len}; - Editing *ed = SEQ_editing_ensure(scene); - - for (int i = 0; i < coords->channel_len && !coords->is_intersecting; i++) { - coords->is_intersecting = SEQ_transform_test_overlap(scene, ed->seqbasep, &dummy_seq); - dummy_seq.machine++; - } + BLI_strncpy(r_path, drag->path, FILE_MAX); } } @@ -256,7 +345,7 @@ static void draw_seq_in_view(bContext *C, wmWindow *UNUSED(win), wmDrag *drag, c mval[0] = xy[0] - region->winrct.xmin; mval[1] = xy[1] - region->winrct.ymin; - update_overlay_strip_poistion_data(C, mval); + float strip_len = update_overlay_strip_position_data(C, mval); GPU_matrix_push(); UI_view2d_view_ortho(®ion->v2d); @@ -280,7 +369,7 @@ static void draw_seq_in_view(bContext *C, wmWindow *UNUSED(win), wmDrag *drag, c /* Draw strips. The code here is taken from sequencer_draw. */ float x1 = coords->start_frame; - float x2 = coords->start_frame + coords->strip_len; + float x2 = coords->start_frame + floorf(strip_len); float strip_color[3]; uchar text_color[4] = {255, 255, 255, 255}; float pixelx = BLI_rctf_size_x(®ion->v2d.cur) / BLI_rcti_size_x(®ion->v2d.mask); @@ -354,21 +443,22 @@ static void draw_seq_in_view(bContext *C, wmWindow *UNUSED(win), wmDrag *drag, c const char *text_array[5]; char text_display[FILE_MAX]; char filename[FILE_MAX]; - char rel_path[FILE_MAX]; + char path[FILE_MAX]; char strip_duration_text[16]; int len_text_arr = 0; + get_drag_path(drag, path); + if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_NAME) { - BLI_split_file_part(drag->path, filename, FILE_MAX); + BLI_split_file_part(path, filename, FILE_MAX); text_array[len_text_arr++] = filename; } if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) { Main *bmain = CTX_data_main(C); - BLI_strncpy(rel_path, drag->path, FILE_MAX); - BLI_path_rel(rel_path, BKE_main_blendfile_path(bmain)); + BLI_path_rel(path, BKE_main_blendfile_path(bmain)); text_array[len_text_arr++] = text_sep; - text_array[len_text_arr++] = rel_path; + text_array[len_text_arr++] = path; } if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION) { @@ -442,6 +532,14 @@ static void prefetch_data_fn(void *custom_data, if (anim != NULL) { g_drop_coords.strip_len = IMB_anim_get_duration(anim, IMB_TC_NONE); + short frs_sec; + float frs_sec_base; + if (IMB_anim_get_fps(anim, &frs_sec, &frs_sec_base, true)) { + g_drop_coords.playback_rate = (float)frs_sec / frs_sec_base; + } + else { + g_drop_coords.playback_rate = 0; + } IMB_free_anim(anim); #ifdef WITH_AUDASPACE /* Try to load sound and see if the video has a sound channel. */ @@ -464,7 +562,7 @@ static void free_prefetch_data_fn(void *custom_data) MEM_freeN(job_data); } -static void start_audio_video_job(bContext *C, char *path, bool only_audio) +static void start_audio_video_job(bContext *C, wmDrag *drag, bool only_audio) { g_drop_coords.strip_len = 0; g_drop_coords.channel_len = 1; @@ -478,8 +576,8 @@ static void start_audio_video_job(bContext *C, char *path, bool only_audio) DropJobData *job_data = (DropJobData *)MEM_mallocN(sizeof(DropJobData), "SeqDragDropPreviewData"); + get_drag_path(drag, job_data->path); - BLI_strncpy(job_data->path, path, FILE_MAX); job_data->only_audio = only_audio; job_data->scene_fps = FPS; @@ -492,15 +590,15 @@ static void start_audio_video_job(bContext *C, char *path, bool only_audio) static void video_prefetch(bContext *C, wmDrag *drag) { - if (drag->type == WM_DRAG_PATH && ELEM(drag->icon, ICON_FILE_MOVIE, ICON_FILE_BLANK)) { - start_audio_video_job(C, drag->path, false); + if (is_movie(drag)) { + start_audio_video_job(C, drag, false); } } static void audio_prefetch(bContext *C, wmDrag *drag) { - if (drag->type == WM_DRAG_PATH && ELEM(drag->icon, ICON_FILE_SOUND, ICON_FILE_BLANK)) { - start_audio_video_job(C, drag->path, true); + if (is_sound(drag)) { + start_audio_video_job(C, drag, true); } } @@ -534,6 +632,7 @@ static void sequencer_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSE SeqDropCoords *coords = drop->draw_data; if (coords) { coords->in_use = false; + coords->has_read_mouse_pos = false; drop->draw_data = NULL; } } diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index eb2e4ef05e5..0bacbde8240 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -519,7 +519,7 @@ static void draw_seq_waveform_overlay( MEM_freeN(waveform_data); } -/* +#if 0 static size_t *waveform_append(WaveVizData *waveform_data, vec2f pos, const float value_min, @@ -529,7 +529,7 @@ static size_t *waveform_append(WaveVizData *waveform_data, const float rms, const bool is_clipping, const bool is_line_strip) -*/ +#endif static void drawmeta_contents(Scene *scene, Sequence *seqm, diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index cb95e9a75de..9313e45a1d4 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2869,7 +2869,7 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "directory", directory); if (is_relative_path) { - /* TODO(campbell): shouldn't this already be relative from the filesel? + /* TODO(@campbellbarton): shouldn't this already be relative from the filesel? * (as the 'filepath' is) for now just make relative here, * but look into changing after 2.60. */ BLI_path_rel(directory, BKE_main_blendfile_path(bmain)); diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c index 6ba1dcc5eb8..af0aa093e40 100644 --- a/source/blender/editors/space_sequencer/sequencer_scopes.c +++ b/source/blender/editors/space_sequencer/sequencer_scopes.c @@ -17,7 +17,7 @@ #include "sequencer_intern.h" -/* XXX(campbell): why is this function better than BLI_math version? +/* XXX(@campbellbarton): why is this function better than BLI_math version? * only difference is it does some normalize after, need to double check on this. */ static void rgb_to_yuv_normalized(const float rgb[3], float yuv[3]) { diff --git a/source/blender/editors/space_text/text_autocomplete.c b/source/blender/editors/space_text/text_autocomplete.c index 54735a4d481..461606f63aa 100644 --- a/source/blender/editors/space_text/text_autocomplete.c +++ b/source/blender/editors/space_text/text_autocomplete.c @@ -314,7 +314,7 @@ static int doc_scroll = 0; static int text_autocomplete_modal(bContext *C, wmOperator *op, const wmEvent *event) { - /* NOTE(campbell): this code could be refactored or rewritten. */ + /* NOTE(@campbellbarton): this code could be refactored or rewritten. */ SpaceText *st = CTX_wm_space_text(C); ScrArea *area = CTX_wm_area(C); ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index a423a842019..1a2eb20d1a9 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -960,7 +960,7 @@ static void view3d_widgets(void) WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera); WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera_view); WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_empty_image); - /* TODO(campbell): Not working well enough, disable for now. */ + /* TODO(@campbellbarton): Not working well enough, disable for now. */ #if 0 WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_armature_spline); #endif @@ -1210,6 +1210,9 @@ static void view3d_main_region_listener(const wmRegionListenerParams *params) break; } break; + case NC_NODE: + ED_region_tag_redraw(region); + break; case NC_WORLD: switch (wmn->data) { case ND_WORLD_DRAW: diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c index 3f6167d92ca..62799dd7a5c 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c @@ -37,7 +37,7 @@ * \{ */ /* - * TODO(campbell): Current conversion is a approximation (usable not correct), + * TODO(@campbellbarton): Current conversion is a approximation (usable not correct), * we'll need to take the next/previous bones into account to get the tangent directions. * First last matrices from 'BKE_pchan_bbone_spline_setup' are close but also not quite accurate * since they're not at either end-points on the curve. diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c index 35d4746608b..6256eeb9621 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.c +++ b/source/blender/editors/space_view3d/view3d_iterators.c @@ -205,6 +205,7 @@ typedef struct foreachScreenObjectVert_userData { void (*func)(void *userData, MVert *mv, const float screen_co[2], int index); void *userData; ViewContext vc; + const bool *hide_vert; eV3DProjTest clip_flag; } foreachScreenObjectVert_userData; @@ -262,18 +263,19 @@ static void meshobject_foreachScreenVert__mapFunc(void *userData, const float UNUSED(no[3])) { foreachScreenObjectVert_userData *data = userData; + if (data->hide_vert && data->hide_vert[index]) { + return; + } struct MVert *mv = &((Mesh *)(data->vc.obact->data))->mvert[index]; - if (!(mv->flag & ME_HIDE)) { - float screen_co[2]; - - if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) != - V3D_PROJ_RET_OK) { - return; - } + float screen_co[2]; - data->func(data->userData, mv, screen_co, index); + if (ED_view3d_project_float_object(data->vc.region, co, screen_co, data->clip_flag) != + V3D_PROJ_RET_OK) { + return; } + + data->func(data->userData, mv, screen_co, index); } void meshobject_foreachScreenVert( @@ -297,6 +299,8 @@ void meshobject_foreachScreenVert( data.func = func; data.userData = userData; data.clip_flag = clip_flag; + data.hide_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert"); if (clip_flag & V3D_PROJ_TEST_CLIP_BB) { ED_view3d_clipping_local(vc->rv3d, vc->obact->obmat); diff --git a/source/blender/editors/space_view3d/view3d_navigate.h b/source/blender/editors/space_view3d/view3d_navigate.h index 721476ace57..925acd90573 100644 --- a/source/blender/editors/space_view3d/view3d_navigate.h +++ b/source/blender/editors/space_view3d/view3d_navigate.h @@ -266,12 +266,12 @@ void ED_view3d_smooth_view(struct bContext *C, * or when calling #ED_view3d_smooth_view_ex. * Otherwise pass in #V3D_SmoothParams.undo_str so an undo step is pushed as needed. */ -void ED_view3d_smooth_view_undo_begin(struct bContext *C, struct ScrArea *area); +void ED_view3d_smooth_view_undo_begin(struct bContext *C, const struct ScrArea *area); /** * Run after multiple smooth-view operations have run to push undo as needed. */ void ED_view3d_smooth_view_undo_end(struct bContext *C, - struct ScrArea *area, + const struct ScrArea *area, const char *undo_str, bool undo_grouped); diff --git a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c index 9af9c5be45a..6b150d1e771 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_smoothview.c +++ b/source/blender/editors/space_view3d/view3d_navigate_smoothview.c @@ -22,13 +22,8 @@ #include "view3d_intern.h" #include "view3d_navigate.h" /* own include */ -static void view3d_smoothview_apply_ex(bContext *C, - View3D *v3d, - ARegion *region, - bool sync_boxview, - bool use_autokey, - const float step, - const bool finished); +static void view3d_smoothview_apply_with_interp( + bContext *C, View3D *v3d, RegionView3D *rv3d, const bool use_autokey, const float factor); /* -------------------------------------------------------------------- */ /** \name Smooth View Undo Handling @@ -45,7 +40,7 @@ static void view3d_smoothview_apply_ex(bContext *C, * operations are executed once smooth-view has started. * \{ */ -void ED_view3d_smooth_view_undo_begin(bContext *C, ScrArea *area) +void ED_view3d_smooth_view_undo_begin(bContext *C, const ScrArea *area) { const View3D *v3d = area->spacedata.first; Object *camera = v3d->camera; @@ -58,11 +53,11 @@ void ED_view3d_smooth_view_undo_begin(bContext *C, ScrArea *area) * NOTE: It doesn't matter if the actual object being manipulated is the camera or not. */ camera->id.tag &= ~LIB_TAG_DOIT; - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + LISTBASE_FOREACH (const ARegion *, region, &area->regionbase) { if (region->regiontype != RGN_TYPE_WINDOW) { continue; } - RegionView3D *rv3d = region->regiondata; + const RegionView3D *rv3d = region->regiondata; if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) { camera->id.tag |= LIB_TAG_DOIT; break; @@ -71,7 +66,7 @@ void ED_view3d_smooth_view_undo_begin(bContext *C, ScrArea *area) } void ED_view3d_smooth_view_undo_end(bContext *C, - ScrArea *area, + const ScrArea *area, const char *undo_str, const bool undo_grouped) { @@ -94,15 +89,15 @@ void ED_view3d_smooth_view_undo_end(bContext *C, * so even in the case there is a quad-view with multiple camera views set, these will all * reference the same camera. In this case it doesn't matter which region is used. * If in the future multiple cameras are supported, this logic can be extended. */ - ARegion *region_camera = NULL; + const ARegion *region_camera = NULL; /* An undo push should be performed. */ bool is_interactive = false; - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + LISTBASE_FOREACH (const ARegion *, region, &area->regionbase) { if (region->regiontype != RGN_TYPE_WINDOW) { continue; } - RegionView3D *rv3d = region->regiondata; + const RegionView3D *rv3d = region->regiondata; if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) { region_camera = region; if (rv3d->sms) { @@ -115,17 +110,13 @@ void ED_view3d_smooth_view_undo_end(bContext *C, return; } - /* Arguments to #view3d_smoothview_apply_ex to temporarily apply transformation. */ - const bool sync_boxview = false; - const bool use_autokey = false; - const bool finished = false; + RegionView3D *rv3d = region_camera->regiondata; /* Fast forward, undo push, then rewind. */ if (is_interactive) { - view3d_smoothview_apply_ex(C, v3d, region_camera, sync_boxview, use_autokey, 1.0f, finished); + view3d_smoothview_apply_with_interp(C, v3d, rv3d, false, 1.0f); } - RegionView3D *rv3d = region_camera->regiondata; if (undo_grouped) { ED_view3d_camera_lock_undo_grouped_push(undo_str, v3d, rv3d, C); } @@ -134,7 +125,7 @@ void ED_view3d_smooth_view_undo_end(bContext *C, } if (is_interactive) { - view3d_smoothview_apply_ex(C, v3d, region_camera, sync_boxview, use_autokey, 0.0f, finished); + view3d_smoothview_apply_with_interp(C, v3d, rv3d, false, 0.0f); } } @@ -397,110 +388,112 @@ void ED_view3d_smooth_view(bContext *C, } } -static void view3d_smoothview_apply_ex(bContext *C, - View3D *v3d, - ARegion *region, - bool sync_boxview, - bool use_autokey, - const float step, - const bool finished) +/** + * Apply with interpolation, on completion run #view3d_smoothview_apply_and_finish. + */ +static void view3d_smoothview_apply_with_interp( + bContext *C, View3D *v3d, RegionView3D *rv3d, const bool use_autokey, const float factor) { - wmWindowManager *wm = CTX_wm_manager(C); - RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore *sms = rv3d->sms; - /* end timer */ - if (finished) { - wmWindow *win = CTX_wm_window(C); + interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, factor); - /* if we went to camera, store the original */ - if (sms->to_camera) { - rv3d->persp = RV3D_CAMOB; - view3d_smooth_view_state_restore(&sms->org, v3d, rv3d); - } - else { - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + if (sms->use_dyn_ofs) { + view3d_orbit_apply_dyn_ofs( + rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs); + } + else { + interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, factor); + } - view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d); + rv3d->dist = interpf(sms->dst.dist, sms->src.dist, factor); + v3d->lens = interpf(sms->dst.lens, sms->src.lens, factor); - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + if (ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d)) { + if (use_autokey) { ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); } + } +} - if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { - rv3d->view = sms->org_view; - } - - MEM_freeN(rv3d->sms); - rv3d->sms = NULL; +/** + * Apply the view-port transformation & free smooth-view related data. + */ +static void view3d_smoothview_apply_and_finish(bContext *C, View3D *v3d, RegionView3D *rv3d) +{ + wmWindowManager *wm = CTX_wm_manager(C); + struct SmoothView3DStore *sms = rv3d->sms; - WM_event_remove_timer(wm, win, rv3d->smooth_timer); - rv3d->smooth_timer = NULL; - rv3d->rflag &= ~RV3D_NAVIGATING; + wmWindow *win = CTX_wm_window(C); - /* Event handling won't know if a UI item has been moved under the pointer. */ - WM_event_add_mousemove(win); + /* if we went to camera, store the original */ + if (sms->to_camera) { + rv3d->persp = RV3D_CAMOB; + view3d_smooth_view_state_restore(&sms->org, v3d, rv3d); } else { - /* ease in/out */ - const float step_inv = 1.0f - step; - - interp_qt_qtqt(rv3d->viewquat, sms->src.quat, sms->dst.quat, step); - - if (sms->use_dyn_ofs) { - view3d_orbit_apply_dyn_ofs( - rv3d->ofs, sms->src.ofs, sms->src.quat, rv3d->viewquat, sms->dyn_ofs); - } - else { - interp_v3_v3v3(rv3d->ofs, sms->src.ofs, sms->dst.ofs, step); - } + const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - rv3d->dist = sms->dst.dist * step + sms->src.dist * step_inv; - v3d->lens = sms->dst.lens * step + sms->src.lens * step_inv; + view3d_smooth_view_state_restore(&sms->dst, v3d, rv3d); - const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d); - if (use_autokey && ED_screen_animation_playing(wm)) { + if (ED_view3d_camera_lock_sync(depsgraph, v3d, rv3d)) { ED_view3d_camera_lock_autokey(v3d, rv3d, C, true, true); } } - if (sync_boxview && (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW)) { - view3d_boxview_copy(CTX_wm_area(C), region); + if ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) { + rv3d->view = sms->org_view; } + MEM_freeN(rv3d->sms); + rv3d->sms = NULL; + + WM_event_remove_timer(wm, win, rv3d->smooth_timer); + rv3d->smooth_timer = NULL; + rv3d->rflag &= ~RV3D_NAVIGATING; + + /* Event handling won't know if a UI item has been moved under the pointer. */ + WM_event_add_mousemove(win); + /* NOTE: this doesn't work right because the v3d->lens is now used in ortho mode r51636, * when switching camera in quad-view the other ortho views would zoom & reset. * * For now only redraw all regions when smooth-view finishes. */ - if (step >= 1.0f) { - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); - } - else { - ED_region_tag_redraw(region); - } + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); } /* only meant for timer usage */ -static void view3d_smoothview_apply(bContext *C, View3D *v3d, ARegion *region, bool sync_boxview) +static void view3d_smoothview_apply_from_timer(bContext *C, View3D *v3d, ARegion *region) { + wmWindowManager *wm = CTX_wm_manager(C); RegionView3D *rv3d = region->regiondata; struct SmoothView3DStore *sms = rv3d->sms; - float step; + float factor; if (sms->time_allowed != 0.0) { - step = (float)((rv3d->smooth_timer->duration) / sms->time_allowed); + factor = (float)((rv3d->smooth_timer->duration) / sms->time_allowed); } else { - step = 1.0f; + factor = 1.0f; + } + if (factor >= 1.0f) { + view3d_smoothview_apply_and_finish(C, v3d, rv3d); } - const bool finished = step >= 1.0f; - if (!finished) { - step = (3.0f * step * step - 2.0f * step * step * step); + else { + /* Ease in/out smoothing. */ + factor = (3.0f * factor * factor - 2.0f * factor * factor * factor); + const bool use_autokey = ED_screen_animation_playing(wm); + view3d_smoothview_apply_with_interp(C, v3d, rv3d, use_autokey, factor); } - view3d_smoothview_apply_ex(C, v3d, region, sync_boxview, true, step, finished); + + if (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXVIEW) { + view3d_boxview_copy(CTX_wm_area(C), region); + } + + ED_region_tag_redraw(region); } static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) @@ -514,7 +507,7 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w return OPERATOR_PASS_THROUGH; } - view3d_smoothview_apply(C, v3d, region, true); + view3d_smoothview_apply_from_timer(C, v3d, region); return OPERATOR_FINISHED; } @@ -522,12 +515,10 @@ static int view3d_smoothview_invoke(bContext *C, wmOperator *UNUSED(op), const w void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region) { RegionView3D *rv3d = region->regiondata; - if (rv3d && rv3d->sms) { - rv3d->sms->time_allowed = 0.0; /* force finishing */ - view3d_smoothview_apply(C, v3d, region, false); + view3d_smoothview_apply_and_finish(C, v3d, rv3d); - /* force update of view matrix so tools that run immediately after + /* Force update of view matrix so tools that run immediately after * can use them without redrawing first */ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 571c39f30cb..763848574ed 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -335,14 +335,16 @@ static bool edbm_backbuf_check_and_select_verts_obmode(Mesh *me, const eSelectOp sel_op) { MVert *mv = me->mvert; - uint index; bool changed = false; const BLI_bitmap *select_bitmap = esel->select_bitmap; if (mv) { - for (index = 0; index < me->totvert; index++, mv++) { - if (!(mv->flag & ME_HIDE)) { + const bool *hide_vert = (const bool *)CustomData_get_layer_named( + &me->vdata, CD_PROP_BOOL, ".hide_vert"); + + for (int index = 0; index < me->totvert; index++, mv++) { + if (!(hide_vert && hide_vert[index])) { const bool is_select = mv->flag & SELECT; const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); @@ -362,14 +364,16 @@ static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me, const eSelectOp sel_op) { MPoly *mpoly = me->mpoly; - uint index; bool changed = false; const BLI_bitmap *select_bitmap = esel->select_bitmap; if (mpoly) { - for (index = 0; index < me->totpoly; index++, mpoly++) { - if (!(mpoly->flag & ME_HIDE)) { + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &me->pdata, CD_PROP_BOOL, ".hide_poly"); + + for (int index = 0; index < me->totpoly; index++, mpoly++) { + if (!(hide_poly && hide_poly[index])) { const bool is_select = mpoly->flag & ME_FACE_SEL; const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index); const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside); @@ -1260,7 +1264,7 @@ static bool do_lasso_select_paintface(ViewContext *vc, } if (changed) { - paintface_flush_flags(vc->C, ob, SELECT); + paintface_flush_flags(vc->C, ob, true, false); } return changed; } @@ -3192,7 +3196,7 @@ static bool do_paintface_box_select(ViewContext *vc, } if (changed) { - paintface_flush_flags(vc->C, vc->obact, SELECT); + paintface_flush_flags(vc->C, vc->obact, true, false); } return changed; } @@ -4093,7 +4097,7 @@ static bool paint_facesel_circle_select(ViewContext *vc, } if (changed) { - paintface_flush_flags(vc->C, ob, SELECT); + paintface_flush_flags(vc->C, ob, true, false); } return changed; } diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index 0d88824a784..5f2a4e8c4cc 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -708,8 +708,11 @@ bool ED_view3d_camera_lock_undo_test(const View3D *v3d, * unnecessary undo steps so undo push for them is not supported for now. Also operators that uses * smooth view for navigation are excluded too, but they can be supported, see: D15345. */ -static bool view3d_camera_lock_undo_ex( - const char *str, View3D *v3d, RegionView3D *rv3d, struct bContext *C, bool undo_group) +static bool view3d_camera_lock_undo_ex(const char *str, + const View3D *v3d, + const RegionView3D *rv3d, + struct bContext *C, + const bool undo_group) { if (ED_view3d_camera_lock_undo_test(v3d, rv3d, C)) { if (undo_group) { @@ -723,14 +726,17 @@ static bool view3d_camera_lock_undo_ex( return false; } -bool ED_view3d_camera_lock_undo_push(const char *str, View3D *v3d, RegionView3D *rv3d, bContext *C) +bool ED_view3d_camera_lock_undo_push(const char *str, + const View3D *v3d, + const RegionView3D *rv3d, + bContext *C) { return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, false); } bool ED_view3d_camera_lock_undo_grouped_push(const char *str, - View3D *v3d, - RegionView3D *rv3d, + const View3D *v3d, + const RegionView3D *rv3d, bContext *C) { return view3d_camera_lock_undo_ex(str, v3d, rv3d, C, true); diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c index b5a85f57b84..f67a44703e5 100644 --- a/source/blender/editors/transform/transform_convert_mesh.c +++ b/source/blender/editors/transform/transform_convert_mesh.c @@ -1314,7 +1314,8 @@ void transform_convert_mesh_crazyspace_detect(TransInfo *t, * correction with \a quats, relative to the coordinates after * the modifiers that support deform matrices \a defcos. */ -#if 0 /* TODO(campbell): fix crazy-space & extrude so it can be enabled for general use. */ +#if 0 /* TODO(@campbellbarton): fix crazy-space & extrude so it can be enabled for general use. \ + */ if ((totleft > 0) || (totleft == -1)) #else if (totleft > 0) @@ -2123,7 +2124,7 @@ void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) FOREACH_TRANS_DATA_CONTAINER (t, tc) { /* table needs to be created for each edit command, since vertices can move etc */ ED_mesh_mirror_spatial_table_end(tc->obedit); - /* TODO(campbell): xform: We need support for many mirror objects at once! */ + /* TODO(@campbellbarton): xform: We need support for many mirror objects at once! */ break; } } diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c index d95bc7b976f..f3bef2c283b 100644 --- a/source/blender/editors/transform/transform_convert_mesh_uv.c +++ b/source/blender/editors/transform/transform_convert_mesh_uv.c @@ -270,7 +270,7 @@ static void createTransUVs(bContext *C, TransInfo *t) continue; } - island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__); + island_center = MEM_callocN(sizeof(*island_center) * elementmap->total_islands, __func__); } BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { @@ -315,9 +315,7 @@ static void createTransUVs(bContext *C, TransInfo *t) } if (is_island_center) { - int i; - - for (i = 0; i < elementmap->totalIslands; i++) { + for (int i = 0; i < elementmap->total_islands; i++) { mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num); mul_v2_v2(island_center[i].co, t->aspect); } diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index 131a7fd517f..a3b5fd2c575 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -261,7 +261,7 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup) copy_m3_m3(ggd->data.normal_mat3, tbounds_normal.axis); } - /* TODO(campbell): run second since this modifies the 3D view, it should not. */ + /* TODO(@campbellbarton): run second since this modifies the 3D view, it should not. */ if (!ED_transform_calc_gizmo_stats(C, &(struct TransformCalcParams){ .orientation_index = ggd->data.orientation_index + 1, diff --git a/source/blender/editors/transform/transform_mode_bend.c b/source/blender/editors/transform/transform_mode_bend.c index acc6b20810f..a48f84ef0bc 100644 --- a/source/blender/editors/transform/transform_mode_bend.c +++ b/source/blender/editors/transform/transform_mode_bend.c @@ -262,7 +262,7 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) +values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle)); } - /* TODO(campbell): xform, compensate object center. */ + /* TODO(@campbellbarton): xform, compensate object center. */ FOREACH_TRANS_DATA_CONTAINER (t, tc) { float warp_sta_local[3]; diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index a3c49d2362f..b48ba0640ad 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -1204,7 +1204,7 @@ void drawEdgeSlide(TransInfo *t) immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); immBegin(GPU_PRIM_LINES, sld->totsv * 2); - /* TODO(campbell): Loop over all verts. */ + /* TODO(@campbellbarton): Loop over all verts. */ sv = sld->sv; for (i = 0; i < sld->totsv; i++, sv++) { float a[3], b[3]; diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 1ccda96fecb..70599c3577c 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -126,19 +126,14 @@ static void constrain_scale_to_boundary(const float numerator, static bool clip_uv_transform_resize(TransInfo *t, float vec[2]) { - /* Check if the current image in UV editor is a tiled image or not. */ - const SpaceImage *sima = t->area->spacedata.first; - const Image *image = sima->image; - const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); /* Stores the coordinates of the closest UDIM tile. * Also acts as an offset to the tile from the origin of UV space. */ float base_offset[2] = {0.0f, 0.0f}; /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ - if (is_tiled_image) { - BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset); - } + const SpaceImage *sima = t->area->spacedata.first; + BKE_image_find_nearest_tile_with_offset(sima->image, t->center_global, base_offset); /* Assume no change is required. */ float scale = 1.0f; diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c index 04a41814b53..8f6ec7bd98f 100644 --- a/source/blender/editors/transform/transform_mode_translate.c +++ b/source/blender/editors/transform/transform_mode_translate.c @@ -437,19 +437,13 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) static bool clip_uv_transform_translation(TransInfo *t, float vec[2]) { - /* Check if the current image in UV editor is a tiled image or not. */ - const SpaceImage *sima = t->area->spacedata.first; - const Image *image = sima->image; - const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); - /* Stores the coordinates of the closest UDIM tile. * Also acts as an offset to the tile from the origin of UV space. */ float base_offset[2] = {0.0f, 0.0f}; /* If tiled image then constrain to correct/closest UDIM tile, else 0-1 UV space. */ - if (is_tiled_image) { - BKE_image_find_nearest_tile_with_offset(image, t->center_global, base_offset); - } + const SpaceImage *sima = t->area->spacedata.first; + BKE_image_find_nearest_tile_with_offset(sima->image, t->center_global, base_offset); float min[2], max[2]; min[0] = min[1] = FLT_MAX; diff --git a/source/blender/editors/transform/transform_snap_object.cc b/source/blender/editors/transform/transform_snap_object.cc index 479214ee2d3..6808f06bdd3 100644 --- a/source/blender/editors/transform/transform_snap_object.cc +++ b/source/blender/editors/transform/transform_snap_object.cc @@ -498,7 +498,8 @@ static bool snap_object_is_snappable(const SnapObjectContext *sctx, const bool is_edited = (base->object->mode == OB_MODE_EDIT); const bool is_selectable = (base->flag & BASE_SELECTABLE); /* Get attributes of state. */ - const bool is_in_object_mode = (base_act == NULL) || (base_act->object->mode == OB_MODE_OBJECT); + const bool is_in_object_mode = (base_act == nullptr) || + (base_act->object->mode == OB_MODE_OBJECT); if (is_in_object_mode) { /* Handle target selection options that make sense for object mode. */ diff --git a/source/blender/editors/undo/CMakeLists.txt b/source/blender/editors/undo/CMakeLists.txt index 284b725cdf0..271d05e9c04 100644 --- a/source/blender/editors/undo/CMakeLists.txt +++ b/source/blender/editors/undo/CMakeLists.txt @@ -11,6 +11,7 @@ set(INC ../../windowmanager ../../../../intern/clog ../../../../intern/guardedalloc + ../../bmesh ) set(SRC diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index ce14e6b180f..bb24bdac690 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -269,7 +269,7 @@ static int ed_undo_step_direction(bContext *C, enum eUndoStepDir step, ReportLis CLOG_INFO(&LOG, 1, "direction=%s", (step == STEP_UNDO) ? "STEP_UNDO" : "STEP_REDO"); - /* TODO(campbell): undo_system: use undo system */ + /* TODO(@campbellbarton): undo_system: use undo system */ /* grease pencil can be can be used in plenty of spaces, so check it first */ /* FIXME: This gpencil undo effectively only supports the one step undo/redo, undo based on name * or index is fully not implemented. diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 3cfe6bd61a4..36a881ec158 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -176,7 +176,7 @@ void ED_editors_init(bContext *C) } } else { - /* TODO(campbell): avoid operator calls. */ + /* TODO(@campbellbarton): avoid operator calls. */ if (obact == ob) { ED_object_mode_set(C, mode); } diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index fc5fb9f9c28..1ebbb0cecc3 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -431,7 +431,7 @@ void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info) immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3fv(color); - /* TODO(campbell): lock to pixels. */ + /* TODO(@campbellbarton): lock to pixels. */ rctf sample_rect_fl; BLI_rctf_init_pt_radius( &sample_rect_fl, diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 04128cf378c..434bfbc64f9 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -14,9 +14,6 @@ struct Scene; struct SpaceImage; struct wmOperatorType; -/* geometric utilities */ -void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len); - /* find nearest */ typedef struct UvNearestHit { diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index 9a31fd6469d..3877a9bb63b 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -259,9 +259,8 @@ static float uv_nearest_image_tile_distance(const Image *image, const float coords[2], float nearest_tile_co[2]) { - if (BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co) == -1) { - zero_v2(nearest_tile_co); - } + BKE_image_find_nearest_tile_with_offset(image, coords, nearest_tile_co); + /* Add 0.5 to get tile center coordinates. */ float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; add_v2_fl(nearest_tile_center_co, 0.5f); @@ -309,23 +308,8 @@ static float uv_nearest_grid_tile_distance(const int udim_grid[2], /* -------------------------------------------------------------------- */ /** \name Calculate UV Islands - * - * \note Currently this is a private API/type, it could be made public. * \{ */ -struct FaceIsland { - struct FaceIsland *next, *prev; - BMFace **faces; - int faces_len; - rctf bounds_rect; - /** - * \note While this is duplicate information, - * it allows islands from multiple meshes to be stored in the same list. - */ - uint cd_loop_uv_offset; - float aspect_y; -}; - struct SharedUVLoopData { uint cd_loop_uv_offset; bool use_seams; @@ -347,14 +331,14 @@ static bool bm_loop_uv_shared_edge_check(const BMLoop *l_a, const BMLoop *l_b, v /** * Calculate islands and add them to \a island_list returning the number of items added. */ -static int bm_mesh_calc_uv_islands(const Scene *scene, - BMesh *bm, - ListBase *island_list, - const bool only_selected_faces, - const bool only_selected_uvs, - const bool use_seams, - const float aspect_y, - const uint cd_loop_uv_offset) +int bm_mesh_calc_uv_islands(const Scene *scene, + BMesh *bm, + ListBase *island_list, + const bool only_selected_faces, + const bool only_selected_uvs, + const bool use_seams, + const float aspect_y, + const uint cd_loop_uv_offset) { int island_added = 0; BM_mesh_elem_table_ensure(bm, BM_FACE); diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 74a9989f550..6755630d3ef 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -189,15 +189,6 @@ void uvedit_live_unwrap_update(SpaceImage *sima, Scene *scene, Object *obedit) /** \name Geometric Utilities * \{ */ -void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len) -{ - int i; - for (i = 0; i < len; i++) { - uv[i][0] = uv_orig[i][0] * aspx; - uv[i][1] = uv_orig[i][1] * aspy; - } -} - bool ED_uvedit_minmax_multi( const Scene *scene, Object **objects_edit, uint objects_len, float r_min[2], float r_max[2]) { @@ -550,14 +541,11 @@ static bool uvedit_uv_straighten(Scene *scene, BMesh *bm, eUVWeldAlign tool) } bool changed = false; - - /* Loop backwards to simplify logic. */ - int j1 = element_map->totalUVs; - for (int i = element_map->totalIslands - 1; i >= 0; --i) { - int j0 = element_map->islandIndices[i]; - changed |= uvedit_uv_straighten_elements( - element_map->buf + j0, j1 - j0, cd_loop_uv_offset, tool); - j1 = j0; + for (int i = 0; i < element_map->total_islands; i++) { + changed |= uvedit_uv_straighten_elements(element_map->storage + element_map->island_indices[i], + element_map->island_total_uvs[i], + cd_loop_uv_offset, + tool); } BM_uv_element_map_free(element_map); diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index d59dcb4f4ed..d88da21ef98 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -81,6 +81,7 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph, typedef enum { UV_SSIM_AREA_UV = 1000, UV_SSIM_AREA_3D, + UV_SSIM_FACE, UV_SSIM_LENGTH_UV, UV_SSIM_LENGTH_3D, UV_SSIM_SIDES, @@ -1421,7 +1422,7 @@ static BMLoop *bm_select_edgeloop_single_side_next(const Scene *scene, scene, l_step, v_from_next, cd_loop_uv_offset); } -/* TODO(campbell): support this in the BMesh API, as we have for clearing other types. */ +/* TODO(@campbellbarton): support this in the BMesh API, as we have for clearing other types. */ static void bm_loop_tags_clear(BMesh *bm) { BMIter iter; @@ -4638,6 +4639,33 @@ static float get_uv_face_needle(const eUVSelectSimilar type, return result; } +static float get_uv_island_needle(const eUVSelectSimilar type, + const struct FaceIsland *island, + const float ob_m3[3][3], + const int cd_loop_uv_offset) + +{ + float result = 0.0f; + switch (type) { + case UV_SSIM_AREA_UV: + for (int i = 0; i < island->faces_len; i++) { + result += BM_face_calc_area_uv(island->faces[i], cd_loop_uv_offset); + } + break; + case UV_SSIM_AREA_3D: + for (int i = 0; i < island->faces_len; i++) { + result += BM_face_calc_area_with_mat3(island->faces[i], ob_m3); + } + break; + case UV_SSIM_FACE: + return island->faces_len; + default: + BLI_assert_unreachable(); + return false; + } + return result; +} + static int uv_select_similar_vert_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -4969,6 +4997,136 @@ static int uv_select_similar_face_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool uv_island_selected(const Scene *scene, struct FaceIsland *island) +{ + BLI_assert(island && island->faces_len); + return uvedit_face_select_test(scene, island->faces[0], island->cd_loop_uv_offset); +} + +static int uv_select_similar_island_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + const eUVSelectSimilar type = RNA_enum_get(op->ptr, "type"); + const float threshold = RNA_float_get(op->ptr, "threshold"); + const eSimilarCmp compare = RNA_enum_get(op->ptr, "compare"); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( + view_layer, ((View3D *)NULL), &objects_len); + + ListBase *island_list_ptr = MEM_callocN(sizeof(*island_list_ptr) * objects_len, __func__); + int island_list_len = 0; + + const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + if (cd_loop_uv_offset == -1) { + continue; + } + + float aspect_y = 1.0f; /* Placeholder value, aspect doesn't change connectivity. */ + island_list_len += bm_mesh_calc_uv_islands(scene, + em->bm, + &island_list_ptr[ob_index], + face_selected, + false, + false, + aspect_y, + cd_loop_uv_offset); + } + + struct FaceIsland **island_array = MEM_callocN(sizeof(*island_array) * island_list_len, + __func__); + + int tree_index = 0; + KDTree_1d *tree_1d = BLI_kdtree_1d_new(island_list_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + if (cd_loop_uv_offset == -1) { + continue; + } + + float ob_m3[3][3]; + copy_m3_m4(ob_m3, obedit->obmat); + + int index; + LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list_ptr[ob_index], index) { + island_array[index] = island; + if (!uv_island_selected(scene, island)) { + continue; + } + float needle = get_uv_island_needle(type, island, ob_m3, cd_loop_uv_offset); + if (tree_1d) { + BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle); + } + } + } + + if (tree_1d != NULL) { + BLI_kdtree_1d_deduplicate(tree_1d); + BLI_kdtree_1d_balance(tree_1d); + } + + int tot_island_index = 0; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + if (cd_loop_uv_offset == -1) { + continue; + } + float ob_m3[3][3]; + copy_m3_m4(ob_m3, obedit->obmat); + + bool changed = false; + int index; + LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list_ptr[ob_index], index) { + island_array[tot_island_index++] = island; /* To deallocate later. */ + if (uv_island_selected(scene, island)) { + continue; + } + float needle = get_uv_island_needle(type, island, ob_m3, cd_loop_uv_offset); + bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare); + if (!select) { + continue; + } + bool do_history = false; + for (int j = 0; j < island->faces_len; j++) { + uvedit_face_select_set( + scene, em, island->faces[j], select, do_history, island->cd_loop_uv_offset); + } + changed = true; + } + + if (changed) { + uv_select_tag_update_for_object(depsgraph, ts, obedit); + } + } + + BLI_assert(tot_island_index == island_list_len); + for (int i = 0; i < island_list_len; i++) { + MEM_SAFE_FREE(island_array[i]->faces); + MEM_SAFE_FREE(island_array[i]); + } + + MEM_SAFE_FREE(island_array); + MEM_SAFE_FREE(island_list_ptr); + MEM_SAFE_FREE(objects); + BLI_kdtree_1d_free(tree_1d); + + return OPERATOR_FINISHED; +} + /* Select similar UV faces/edges/verts based on current selection. */ static int uv_select_similar_exec(bContext *C, wmOperator *op) { @@ -4990,7 +5148,7 @@ static int uv_select_similar_exec(bContext *C, wmOperator *op) return uv_select_similar_face_exec(C, op); } if (selectmode & UV_SELECT_ISLAND) { - // return uv_select_similar_island_exec(C, op); + return uv_select_similar_island_exec(C, op); } return uv_select_similar_vert_exec(C, op); @@ -5011,6 +5169,12 @@ static EnumPropertyItem prop_face_similar_types[] = { {UV_SSIM_MATERIAL, "MATERIAL", 0, "Material", ""}, {0}}; +static EnumPropertyItem prop_island_similar_types[] = { + {UV_SSIM_AREA_UV, "AREA", 0, "Area", ""}, + {UV_SSIM_AREA_3D, "AREA_3D", 0, "Area 3D", ""}, + {UV_SSIM_FACE, "FACE", 0, "Amount of Faces in Island", ""}, + {0}}; + static EnumPropertyItem prop_similar_compare_types[] = {{SIM_CMP_EQ, "EQUAL", 0, "Equal", ""}, {SIM_CMP_GT, "GREATER", 0, "Greater", ""}, {SIM_CMP_LT, "LESS", 0, "Less", ""}, @@ -5030,6 +5194,9 @@ static const EnumPropertyItem *uv_select_similar_type_itemf(bContext *C, if (selectmode & UV_SELECT_FACE) { return prop_face_similar_types; } + if (selectmode & UV_SELECT_ISLAND) { + return prop_island_similar_types; + } } return prop_vert_similar_types; @@ -5228,7 +5395,7 @@ static void uv_isolate_selected_islands(const Scene *scene, return; } - int num_islands = elementmap->totalIslands; + int num_islands = elementmap->total_islands; /* Boolean array that tells if island with index i is completely selected or not. */ bool *is_island_not_selected = MEM_callocN(sizeof(bool) * (num_islands), __func__); diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index 579674930a6..4a2ea5c3aa6 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -287,14 +287,6 @@ static void stitch_update_header(StitchStateContainer *ssc, bContext *C) } } -static int getNumOfIslandUvs(UvElementMap *elementMap, int island) -{ - if (island == elementMap->totalIslands - 1) { - return elementMap->totalUVs - elementMap->islandIndices[island]; - } - return elementMap->islandIndices[island + 1] - elementMap->islandIndices[island]; -} - static void stitch_uv_rotate(const float mat[2][2], const float medianPoint[2], float uv[2], @@ -419,10 +411,9 @@ static void stitch_calculate_island_snapping(StitchState *state, int final) { BMesh *bm = state->em->bm; - int i; UvElement *element; - for (i = 0; i < state->element_map->totalIslands; i++) { + for (int i = 0; i < state->element_map->total_islands; i++) { if (island_stitch_data[i].addedForPreview) { int numOfIslandUVs = 0, j; int totelem = island_stitch_data[i].num_rot_elements_neg + @@ -464,8 +455,8 @@ static void stitch_calculate_island_snapping(StitchState *state, } angle_to_mat2(rotation_mat, rotation); - numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); - element = &state->element_map->buf[state->element_map->islandIndices[i]]; + numOfIslandUVs = state->element_map->island_total_uvs[i]; + element = &state->element_map->storage[state->element_map->island_indices[i]]; for (j = 0; j < numOfIslandUVs; j++, element++) { /* stitchable uvs have already been processed, don't process */ if (!(element->flag & STITCH_PROCESSED)) { @@ -527,8 +518,8 @@ static void stitch_island_calculate_edge_rotation(UvEdge *edge, luv2 = CustomData_bmesh_get(&bm->ldata, element2->l->head.data, CD_MLOOPUV); if (ssc->mode == STITCH_VERT) { - index1 = uvfinal_map[element1 - state->element_map->buf]; - index2 = uvfinal_map[element2 - state->element_map->buf]; + index1 = uvfinal_map[element1 - state->element_map->storage]; + index2 = uvfinal_map[element2 - state->element_map->storage]; } else { index1 = edge->uv1; @@ -569,27 +560,17 @@ static void stitch_island_calculate_vert_rotation(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data) { - float edgecos = 1.0f, edgesin = 0.0f; - int index; - UvElement *element_iter; float rotation = 0, rotation_neg = 0; int rot_elem = 0, rot_elem_neg = 0; - BMLoop *l; if (element->island == ssc->static_island && !ssc->midpoints) { return; } - l = element->l; - - index = BM_elem_index_get(l->v); - - element_iter = state->element_map->vert[index]; - + UvElement *element_iter = BM_uv_element_get_head(state->element_map, element); for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate && stitch_check_uvs_state_stitchable(element, element_iter, ssc, state)) { - int index_tmp1, index_tmp2; float normal[2]; /* only calculate rotation against static island uv verts */ @@ -597,14 +578,14 @@ static void stitch_island_calculate_vert_rotation(UvElement *element, continue; } - index_tmp1 = element_iter - state->element_map->buf; + int index_tmp1 = element_iter - state->element_map->storage; index_tmp1 = state->map[index_tmp1]; - index_tmp2 = element - state->element_map->buf; + int index_tmp2 = element - state->element_map->storage; index_tmp2 = state->map[index_tmp2]; negate_v2_v2(normal, state->normals + index_tmp2 * 2); - edgecos = dot_v2v2(normal, state->normals + index_tmp1 * 2); - edgesin = cross_v2v2(normal, state->normals + index_tmp1 * 2); + float edgecos = dot_v2v2(normal, state->normals + index_tmp1 * 2); + float edgesin = cross_v2v2(normal, state->normals + index_tmp1 * 2); if (edgesin > 0.0f) { rotation += acosf(max_ff(-1.0f, min_ff(1.0f, edgecos))); rot_elem++; @@ -653,9 +634,8 @@ static void state_delete(StitchState *state) if (state->edges) { MEM_freeN(state->edges); } - if (state->stitch_preview) { - stitch_preview_delete(state->stitch_preview); - } + stitch_preview_delete(state->stitch_preview); + state->stitch_preview = NULL; if (state->edge_hash) { BLI_ghash_free(state->edge_hash, NULL, NULL); } @@ -680,10 +660,7 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState * UvEdge *edges = state->edges; const int *map = state->map; UvElementMap *element_map = state->element_map; - UvElement *first_element = element_map->buf; - int i; - - for (i = 0; i < state->total_separate_edges; i++) { + for (int i = 0; i < state->total_separate_edges; i++) { UvEdge *edge = edges + i; if (edge->first) { @@ -696,7 +673,7 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState * UvElement *element2 = state->uvs[edge->uv2]; /* Now iterate through all faces and try to find edges sharing the same vertices */ - UvElement *iter1 = element_map->vert[BM_elem_index_get(element1->l->v)]; + UvElement *iter1 = BM_uv_element_get_head(state->element_map, element1); UvEdge *last_set = edge; int elemindex2 = BM_elem_index_get(element2->l->v); @@ -714,8 +691,8 @@ static void stitch_uv_edge_generate_linked_edges(GHash *edge_hash, StitchState * } if (iter2) { - int index1 = map[iter1 - first_element]; - int index2 = map[iter2 - first_element]; + int index1 = map[iter1 - element_map->storage]; + int index2 = map[iter2 - element_map->storage]; UvEdge edgetmp; UvEdge *edge2, *eiter; bool valid = true; @@ -764,15 +741,7 @@ static void determine_uv_stitchability(UvElement *element, StitchState *state, IslandStitchData *island_stitch_data) { - int vert_index; - UvElement *element_iter; - BMLoop *l; - - l = element->l; - - vert_index = BM_elem_index_get(l->v); - element_iter = state->element_map->vert[vert_index]; - + UvElement *element_iter = BM_uv_element_get_head(state->element_map, element); for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate) { if (stitch_check_uvs_stitchable(element, element_iter, ssc, state)) { @@ -853,16 +822,7 @@ static void stitch_validate_uv_stitchability(UvElement *element, return; } - UvElement *element_iter; - int vert_index; - BMLoop *l; - - l = element->l; - - vert_index = BM_elem_index_get(l->v); - - element_iter = state->element_map->vert[vert_index]; - + UvElement *element_iter = BM_uv_element_get_head(state->element_map, element); for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate) { if (element_iter == element) { @@ -1015,7 +975,7 @@ static int stitch_process_data(StitchStateContainer *ssc, preview_position[i].data_position = STITCH_NO_PREVIEW; } - island_stitch_data = MEM_callocN(sizeof(*island_stitch_data) * state->element_map->totalIslands, + island_stitch_data = MEM_callocN(sizeof(*island_stitch_data) * state->element_map->total_islands, "stitch_island_data"); if (!island_stitch_data) { return 0; @@ -1040,7 +1000,7 @@ static int stitch_process_data(StitchStateContainer *ssc, } /* Remember stitchable candidates as places the 'I' button will stop at. */ - for (int island_idx = 0; island_idx < state->element_map->totalIslands; island_idx++) { + for (int island_idx = 0; island_idx < state->element_map->total_islands; island_idx++) { state->island_is_stitchable[island_idx] = island_stitch_data[island_idx].stitchableCandidate ? true : false; @@ -1048,10 +1008,10 @@ static int stitch_process_data(StitchStateContainer *ssc, if (is_active_state) { /* set static island to one that is added for preview */ - ssc->static_island %= state->element_map->totalIslands; + ssc->static_island %= state->element_map->total_islands; while (!(island_stitch_data[ssc->static_island].stitchableCandidate)) { ssc->static_island++; - ssc->static_island %= state->element_map->totalIslands; + ssc->static_island %= state->element_map->total_islands; /* this is entirely possible if for example limit stitching * with no stitchable verts or no selection */ if (ssc->static_island == previous_island) { @@ -1172,13 +1132,11 @@ static int stitch_process_data(StitchStateContainer *ssc, * Setup preview for stitchable islands * ****************************************/ if (ssc->snap_islands) { - for (i = 0; i < state->element_map->totalIslands; i++) { + for (i = 0; i < state->element_map->total_islands; i++) { if (island_stitch_data[i].addedForPreview) { - int numOfIslandUVs = 0, j; - UvElement *element; - numOfIslandUVs = getNumOfIslandUvs(state->element_map, i); - element = &state->element_map->buf[state->element_map->islandIndices[i]]; - for (j = 0; j < numOfIslandUVs; j++, element++) { + int numOfIslandUVs = state->element_map->island_total_uvs[i]; + UvElement *element = &state->element_map->storage[state->element_map->island_indices[i]]; + for (int j = 0; j < numOfIslandUVs; j++, element++) { stitch_set_face_preview_buffer_position(element->l->f, preview, preview_position); } } @@ -1263,7 +1221,7 @@ static int stitch_process_data(StitchStateContainer *ssc, if (ssc->mode == STITCH_VERT) { final_position = MEM_callocN(state->selection_size * sizeof(*final_position), "stitch_uv_average"); - uvfinal_map = MEM_mallocN(state->element_map->totalUVs * sizeof(*uvfinal_map), + uvfinal_map = MEM_mallocN(state->element_map->total_uvs * sizeof(*uvfinal_map), "stitch_uv_final_map"); } else { @@ -1279,12 +1237,11 @@ static int stitch_process_data(StitchStateContainer *ssc, if (element->flag & STITCH_STITCHABLE) { BMLoop *l; MLoopUV *luv; - UvElement *element_iter; l = element->l; luv = CustomData_bmesh_get(&bm->ldata, l->head.data, CD_MLOOPUV); - uvfinal_map[element - state->element_map->buf] = i; + uvfinal_map[element - state->element_map->storage] = i; copy_v2_v2(final_position[i].uv, luv->uv); final_position[i].count = 1; @@ -1293,8 +1250,7 @@ static int stitch_process_data(StitchStateContainer *ssc, continue; } - element_iter = state->element_map->vert[BM_elem_index_get(l->v)]; - + UvElement *element_iter = state->element_map->vertex[BM_elem_index_get(l->v)]; for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate) { if (stitch_check_uvs_state_stitchable(element, element_iter, ssc, state)) { @@ -1542,6 +1498,7 @@ static int stitch_process_data_all(StitchStateContainer *ssc, Scene *scene, int static uint uv_edge_hash(const void *key) { const UvEdge *edge = key; + BLI_assert(edge->uv1 < edge->uv2); return (BLI_ghashutil_uinthash(edge->uv2) + BLI_ghashutil_uinthash(edge->uv1)); } @@ -1549,6 +1506,8 @@ static bool uv_edge_compare(const void *a, const void *b) { const UvEdge *edge1 = a; const UvEdge *edge2 = b; + BLI_assert(edge1->uv1 < edge1->uv2); + BLI_assert(edge2->uv1 < edge2->uv2); if ((edge1->uv1 == edge2->uv1) && (edge1->uv2 == edge2->uv2)) { return 0; @@ -1588,13 +1547,8 @@ static void stitch_select_edge(UvEdge *edge, StitchState *state, int always_sele /* Select all common uvs */ static void stitch_select_uv(UvElement *element, StitchState *state, int always_select) { - BMLoop *l; - UvElement *element_iter; UvElement **selection_stack = (UvElement **)state->selection_stack; - - l = element->l; - - element_iter = state->element_map->vert[BM_elem_index_get(l->v)]; + UvElement *element_iter = BM_uv_element_get_head(state->element_map, element); /* first deselect all common uvs */ for (; element_iter; element_iter = element_iter->next) { if (element_iter->separate) { @@ -1850,8 +1804,8 @@ static UvEdge *uv_edge_get(BMLoop *l, StitchState *state) UvElement *element1 = BM_uv_element_get(state->element_map, l->f, l); UvElement *element2 = BM_uv_element_get(state->element_map, l->f, l->next); - int uv1 = state->map[element1 - state->element_map->buf]; - int uv2 = state->map[element2 - state->element_map->buf]; + int uv1 = state->map[element1 - state->element_map->storage]; + int uv2 = state->map[element2 - state->element_map->storage]; if (uv1 < uv2) { tmp_edge.uv1 = uv1; @@ -1878,7 +1832,6 @@ static StitchState *stitch_init(bContext *C, int total_edges; /* maps uvelements to their first coincident uv */ int *map; - int counter = 0, i; BMFace *efa; BMLoop *l; BMIter iter, liter; @@ -1913,45 +1866,39 @@ static StitchState *stitch_init(bContext *C, ED_uvedit_get_aspect(obedit, &aspx, &aspy); state->aspect = aspx / aspy; - /* Count 'unique' uvs */ - for (i = 0; i < state->element_map->totalUVs; i++) { - if (state->element_map->buf[i].separate) { - counter++; - } - } + int unique_uvs = state->element_map->total_unique_uvs; + state->total_separate_uvs = unique_uvs; - /* explicitly set preview to NULL, - * to avoid deleting an invalid pointer on stitch_process_data */ - state->stitch_preview = NULL; /* Allocate the unique uv buffers */ - state->uvs = MEM_mallocN(sizeof(*state->uvs) * counter, "uv_stitch_unique_uvs"); + state->uvs = MEM_mallocN(sizeof(*state->uvs) * unique_uvs, "uv_stitch_unique_uvs"); /* internal uvs need no normals but it is hard and slow to keep a map of - * normals only for boundary uvs, so allocating for all uvs */ - state->normals = MEM_callocN(sizeof(*state->normals) * counter * 2, "uv_stitch_normals"); - state->total_separate_uvs = counter; - state->map = map = MEM_mallocN(sizeof(*map) * state->element_map->totalUVs, + * normals only for boundary uvs, so allocating for all uvs. + * Times 2 because each `float[2]` is stored as `{n[2 * i], n[2*i + 1]}`. */ + state->normals = MEM_callocN(sizeof(*state->normals) * 2 * unique_uvs, "uv_stitch_normals"); + state->map = map = MEM_mallocN(sizeof(*map) * state->element_map->total_uvs, "uv_stitch_unique_map"); /* Allocate the edge stack */ edge_hash = BLI_ghash_new(uv_edge_hash, uv_edge_compare, "stitch_edge_hash"); - all_edges = MEM_mallocN(sizeof(*all_edges) * state->element_map->totalUVs, "ssc_edges"); + all_edges = MEM_mallocN(sizeof(*all_edges) * state->element_map->total_uvs, "ssc_edges"); + BLI_assert(!state->stitch_preview); /* Paranoia. */ if (!state->uvs || !map || !edge_hash || !all_edges) { state_delete(state); return NULL; } - /* So that we can use this as index for the UvElements */ - counter = -1; + /* Index for the UvElements. */ + int counter = -1; /* initialize the unique UVs and map */ - for (i = 0; i < em->bm->totvert; i++) { - UvElement *element = state->element_map->vert[i]; + for (int i = 0; i < em->bm->totvert; i++) { + UvElement *element = state->element_map->vertex[i]; for (; element; element = element->next) { if (element->separate) { counter++; state->uvs[counter] = element; } /* Pointer arithmetic to the rescue, as always :). */ - map[element - state->element_map->buf] = counter; + map[element - state->element_map->storage] = counter; } } @@ -1965,13 +1912,13 @@ static StitchState *stitch_init(bContext *C, BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { UvElement *element = BM_uv_element_get(state->element_map, efa, l); - int offset1, itmp1 = element - state->element_map->buf; - int offset2, - itmp2 = BM_uv_element_get(state->element_map, efa, l->next) - state->element_map->buf; + int itmp1 = element - state->element_map->storage; + int itmp2 = BM_uv_element_get(state->element_map, efa, l->next) - + state->element_map->storage; UvEdge *edge; - offset1 = map[itmp1]; - offset2 = map[itmp2]; + int offset1 = map[itmp1]; + int offset2 = map[itmp2]; all_edges[counter].next = NULL; all_edges[counter].first = NULL; @@ -2012,7 +1959,7 @@ static StitchState *stitch_init(bContext *C, state->total_separate_edges = total_edges; /* fill the edges with data */ - i = 0; + int i = 0; GHASH_ITER (gh_iter, edge_hash) { edges[i++] = *((UvEdge *)BLI_ghashIterator_getKey(&gh_iter)); } @@ -2091,13 +2038,13 @@ static StitchState *stitch_init(bContext *C, efa = BM_face_at_index(em->bm, faceIndex); element = BM_uv_element_get( state->element_map, efa, BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, elementIndex)); - uv1 = map[element - state->element_map->buf]; + uv1 = map[element - state->element_map->storage]; element = BM_uv_element_get( state->element_map, efa, BM_iter_at_index(NULL, BM_LOOPS_OF_FACE, efa, (elementIndex + 1) % efa->len)); - uv2 = map[element - state->element_map->buf]; + uv2 = map[element - state->element_map->storage]; if (uv1 < uv2) { tmp_edge.uv1 = uv1; @@ -2162,8 +2109,8 @@ static StitchState *stitch_init(bContext *C, /***** initialize static island preview data *****/ state->tris_per_island = MEM_mallocN( - sizeof(*state->tris_per_island) * state->element_map->totalIslands, "stitch island tris"); - for (i = 0; i < state->element_map->totalIslands; i++) { + sizeof(*state->tris_per_island) * state->element_map->total_islands, "stitch island tris"); + for (i = 0; i < state->element_map->total_islands; i++) { state->tris_per_island[i] = 0; } @@ -2175,7 +2122,7 @@ static StitchState *stitch_init(bContext *C, } } - state->island_is_stitchable = MEM_callocN(sizeof(bool) * state->element_map->totalIslands, + state->island_is_stitchable = MEM_callocN(sizeof(bool) * state->element_map->total_islands, "stitch I stops"); if (!state->island_is_stitchable) { state_delete(state); @@ -2199,7 +2146,7 @@ static bool goto_next_island(StitchStateContainer *ssc) do { ssc->static_island++; - if (ssc->static_island >= active_state->element_map->totalIslands) { + if (ssc->static_island >= active_state->element_map->total_islands) { /* go to next object */ ssc->active_object_index++; ssc->active_object_index %= ssc->objects_len; @@ -2349,7 +2296,7 @@ static int stitch_init_all(bContext *C, wmOperator *op) ssc->static_island = RNA_int_get(op->ptr, "static_island"); StitchState *state = ssc->states[ssc->active_object_index]; - ssc->static_island %= state->element_map->totalIslands; + ssc->static_island %= state->element_map->total_islands; /* If the initial active object doesn't have any stitchable islands * then no active island will be seen in the UI. diff --git a/source/blender/freestyle/intern/view_map/ViewMap.cpp b/source/blender/freestyle/intern/view_map/ViewMap.cpp index b26a833b32e..d918cfec2ae 100644 --- a/source/blender/freestyle/intern/view_map/ViewMap.cpp +++ b/source/blender/freestyle/intern/view_map/ViewMap.cpp @@ -398,7 +398,7 @@ void TVertex::setBackEdgeB(ViewEdge *iBackEdgeB, bool incoming) void TVertex::Replace(ViewEdge *iOld, ViewEdge *iNew) { - // theoritically, we only replace edges for which this + // theoretically, we only replace edges for which this // view vertex is the B vertex if ((iOld == _FrontEdgeA.first) && (_FrontEdgeA.first->B() == this)) { _FrontEdgeA.first = iNew; diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index da83d9e8957..0f06890cbfa 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -27,7 +27,7 @@ set(SRC intern/reverse_uv_sampler.cc intern/set_curve_type.cc intern/subdivide_curves.cc - intern/uv_parametrizer.c + intern/uv_parametrizer.cc GEO_add_curves_on_mesh.hh GEO_fillet_curves.hh diff --git a/source/blender/geometry/GEO_uv_parametrizer.h b/source/blender/geometry/GEO_uv_parametrizer.h index 5285aefbd4c..ff110f18ffb 100644 --- a/source/blender/geometry/GEO_uv_parametrizer.h +++ b/source/blender/geometry/GEO_uv_parametrizer.h @@ -13,8 +13,8 @@ extern "C" { #endif typedef struct ParamHandle ParamHandle; /* Handle to an array of charts. */ -typedef intptr_t ParamKey; /* Key (hash) for identifying verts and faces. */ -#define PARAM_KEY_MAX INTPTR_MAX +typedef uintptr_t ParamKey; /* Key (hash) for identifying verts and faces. */ +#define PARAM_KEY_MAX UINTPTR_MAX /* -------------------------------------------------------------------- */ /** \name Chart Construction: diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index 7184d774a22..299040d4d32 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_length_parameterize.hh" +#include "BLI_task.hh" #include "BKE_attribute_math.hh" #include "BKE_mesh_sample.hh" @@ -102,8 +103,8 @@ void interpolate_from_neighbors(const Span<NeighborCurves> neighbors_per_curve, } } } + mixer.finalize(range); }); - mixer.finalize(); } static void interpolate_position_without_interpolation( diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index d61941aa071..e252e28805e 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -154,7 +154,7 @@ static void gather_point_attributes_to_interpolate(const CurveComponent &src_com retrieve_attribute_spans( ids, src_component, dst_component, result.src, result.dst, result.dst_attributes); - /* Attributes that aren't interpolated like Bezier handles still have to be be copied + /* Attributes that aren't interpolated like Bezier handles still have to be copied * to the result when there are any unselected curves of the corresponding type. */ retrieve_attribute_spans(ids_no_interpolation, src_component, diff --git a/source/blender/geometry/intern/set_curve_type.cc b/source/blender/geometry/intern/set_curve_type.cc index 40ee2488a4b..92609a45bdc 100644 --- a/source/blender/geometry/intern/set_curve_type.cc +++ b/source/blender/geometry/intern/set_curve_type.cc @@ -286,42 +286,6 @@ static void retrieve_curve_sizes(const bke::CurvesGeometry &curves, MutableSpan< }); } -struct GenericAttributes : NonCopyable, NonMovable { - Vector<GSpan> src; - Vector<GMutableSpan> dst; - - Vector<bke::GSpanAttributeWriter> attributes; -}; - -static void retrieve_generic_point_attributes(const bke::AttributeAccessor &src_attributes, - bke::MutableAttributeAccessor &dst_attributes, - GenericAttributes &attributes) -{ - src_attributes.for_all( - [&](const bke::AttributeIDRef &id, const bke::AttributeMetaData meta_data) { - if (meta_data.domain != ATTR_DOMAIN_POINT) { - /* Curve domain attributes are all copied directly to the result in one step. */ - return true; - } - if (src_attributes.is_builtin(id)) { - if (!(id.is_named() && ELEM(id, "tilt", "radius"))) { - return true; - } - } - - GVArray src_attribute = src_attributes.lookup(id, ATTR_DOMAIN_POINT); - BLI_assert(src_attribute); - attributes.src.append(src_attribute.get_internal_span()); - - bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_span( - id, ATTR_DOMAIN_POINT, meta_data.data_type); - attributes.dst.append(dst_attribute.span); - attributes.attributes.append(std::move(dst_attribute)); - - return true; - }); -} - static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &src_curves, const IndexMask selection) { @@ -347,8 +311,16 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s const bke::AttributeAccessor src_attributes = src_curves.attributes(); bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); - GenericAttributes attributes; - retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes); + Vector<bke::AttributeTransferData> generic_attributes = bke::retrieve_attributes_for_transfer( + src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", + "handle_type_left", + "handle_type_right", + "handle_right", + "handle_left", + "nurbs_weight"}); MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write(); @@ -373,9 +345,9 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s } }); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -384,9 +356,9 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_l); bke::curves::fill_points<int8_t>(dst_curves, selection, BEZIER_HANDLE_VECTOR, dst_types_r); dst_curves.calculate_bezier_auto_handles(); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -404,9 +376,9 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s dst_curves.calculate_bezier_auto_handles(); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, selection, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -445,14 +417,14 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s } }); - for (const int i_attribute : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i : selection.slice(range)) { const IndexRange src_points = src_curves.points_for_curve(i); const IndexRange dst_points = dst_curves.points_for_curve(i); - nurbs_to_bezier_assign(attributes.src[i_attribute].slice(src_points), + nurbs_to_bezier_assign(attribute.src.slice(src_points), KnotsMode(src_knot_modes[i]), - attributes.dst[i_attribute].slice(dst_points)); + attribute.dst.span.slice(dst_points)); } }); } @@ -469,13 +441,13 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( src_curves.curves_range()); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); } - for (bke::GSpanAttributeWriter &attribute : attributes.attributes) { - attribute.finish(); + for (bke::AttributeTransferData &attribute : generic_attributes) { + attribute.dst.finish(); } return dst_curves; @@ -504,8 +476,16 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr const bke::AttributeAccessor src_attributes = src_curves.attributes(); bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); - GenericAttributes attributes; - retrieve_generic_point_attributes(src_attributes, dst_attributes, attributes); + Vector<bke::AttributeTransferData> generic_attributes = bke::retrieve_attributes_for_transfer( + src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", + "handle_type_left", + "handle_type_right", + "handle_right", + "handle_left", + "nurbs_weight"}); MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); @@ -529,13 +509,13 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr } }); - for (const int i_attribute : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i : selection.slice(range)) { const IndexRange src_points = src_curves.points_for_curve(i); const IndexRange dst_points = dst_curves.points_for_curve(i); - bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points), - attributes.dst[i_attribute].slice(dst_points)); + bezier_generic_to_nurbs(attribute.src.slice(src_points), + attribute.dst.span.slice(dst_points)); } }); } @@ -563,12 +543,9 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr }); } - for (const int i_attribute : attributes.src.index_range()) { - bke::curves::copy_point_data(src_curves, - dst_curves, - selection, - attributes.src[i_attribute], - attributes.dst[i_attribute]); + for (bke::AttributeTransferData &attribute : generic_attributes) { + bke::curves::copy_point_data( + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -591,13 +568,13 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr } }); - for (const int i_attribute : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { for (const int i : selection.slice(range)) { const IndexRange src_points = src_curves.points_for_curve(i); const IndexRange dst_points = dst_curves.points_for_curve(i); - bezier_generic_to_nurbs(attributes.src[i_attribute].slice(src_points), - attributes.dst[i_attribute].slice(dst_points)); + bezier_generic_to_nurbs(attribute.src.slice(src_points), + attribute.dst.span.slice(dst_points)); } }); } @@ -614,12 +591,9 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr dst_curves.nurbs_weights_for_write()); } - for (const int i_attribute : attributes.src.index_range()) { - bke::curves::copy_point_data(src_curves, - dst_curves, - selection, - attributes.src[i_attribute], - attributes.dst[i_attribute]); + for (bke::AttributeTransferData &attribute : generic_attributes) { + bke::curves::copy_point_data( + src_curves, dst_curves, selection, attribute.src, attribute.dst.span); } }; @@ -634,13 +608,13 @@ static bke::CurvesGeometry convert_curves_to_nurbs(const bke::CurvesGeometry &sr const Vector<IndexRange> unselected_ranges = selection.extract_ranges_invert( src_curves.curves_range()); - for (const int i : attributes.src.index_range()) { + for (bke::AttributeTransferData &attribute : generic_attributes) { bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); } - for (bke::GSpanAttributeWriter &attribute : attributes.attributes) { - attribute.finish(); + for (bke::AttributeTransferData &attribute : generic_attributes) { + attribute.dst.finish(); } return dst_curves; diff --git a/source/blender/geometry/intern/uv_parametrizer.c b/source/blender/geometry/intern/uv_parametrizer.cc index 38924c718c3..b7526d82ecc 100644 --- a/source/blender/geometry/intern/uv_parametrizer.c +++ b/source/blender/geometry/intern/uv_parametrizer.cc @@ -30,7 +30,7 @@ /* Special Purpose Hash */ -typedef intptr_t PHashKey; +typedef uintptr_t PHashKey; typedef struct PHashLink { struct PHashLink *next; @@ -45,24 +45,22 @@ typedef struct PHash { /* Simplices */ -typedef struct PVert { +struct PVert { struct PVert *nextlink; union PVertUnion { PHashKey key; /* Construct. */ int id; /* ABF/LSCM matrix index. */ - float distortion; /* Area smoothing. */ HeapNode *heaplink; /* Edge collapsing. */ } u; struct PEdge *edge; float co[3]; float uv[2]; - uchar flag; - -} PVert; + uint flag; +}; -typedef struct PEdge { +struct PEdge { struct PEdge *nextlink; union PEdgeUnion { @@ -77,11 +75,10 @@ typedef struct PEdge { struct PEdge *next; struct PFace *face; float *orig_uv, old_uv[2]; - ushort flag; - -} PEdge; + uint flag; +}; -typedef struct PFace { +struct PFace { struct PFace *nextlink; union PFaceUnion { @@ -92,8 +89,8 @@ typedef struct PFace { } u; struct PEdge *edge; - uchar flag; -} PFace; + uint flag; +}; enum PVertFlag { PVERT_PIN = 1, @@ -126,7 +123,7 @@ enum PFaceFlag { /* Chart */ -typedef struct PChart { +struct PChart { PVert *verts; PEdge *edges; PFace *faces; @@ -153,12 +150,7 @@ typedef struct PChart { } pack; } u; - uchar flag; - ParamHandle *handle; -} PChart; - -enum PChartFlag { - PCHART_HAS_PINS = 1, + bool has_pins; }; enum PHandleState { @@ -168,7 +160,7 @@ enum PHandleState { PHANDLE_STATE_STRETCH, }; -typedef struct ParamHandle { +struct ParamHandle { enum PHandleState state; MemArena *arena; MemArena *polyfill_arena; @@ -189,7 +181,7 @@ typedef struct ParamHandle { RNG *rng; float blend; -} ParamHandle; +}; /* PHash * - special purpose hash that keeps all its elements in a single linked list. @@ -225,8 +217,10 @@ static PHash *phash_new(PHashLink **list, int sizehint) static void phash_delete(PHash *ph) { - MEM_freeN(ph->buckets); - MEM_freeN(ph); + if (ph) { + MEM_SAFE_FREE(ph->buckets); + MEM_freeN(ph); + } } static int phash_size(PHash *ph) @@ -240,7 +234,7 @@ static void phash_insert(PHash *ph, PHashLink *link) uintptr_t hash = PHASH_hash(ph, link->key); PHashLink *lookup = ph->buckets[hash]; - if (lookup == NULL) { + if (lookup == nullptr) { /* insert in front of the list */ ph->buckets[hash] = link; link->next = *(ph->list); @@ -255,13 +249,13 @@ static void phash_insert(PHash *ph, PHashLink *link) ph->size++; if (ph->size > (size * 3)) { - PHashLink *next = NULL, *first = *(ph->list); + PHashLink *next = nullptr, *first = *(ph->list); ph->cursize = PHashSizes[++ph->cursize_id]; MEM_freeN(ph->buckets); ph->buckets = (PHashLink **)MEM_callocN(ph->cursize * sizeof(*ph->buckets), "PHashBuckets"); ph->size = 0; - *(ph->list) = NULL; + *(ph->list) = nullptr; for (link = first; link; link = next) { next = link->next; @@ -280,7 +274,7 @@ static PHashLink *phash_lookup(PHash *ph, PHashKey key) return link; } if (PHASH_hash(ph, link->key) != hash) { - return NULL; + return nullptr; } } @@ -296,7 +290,7 @@ static PHashLink *phash_next(PHash *ph, PHashKey key, PHashLink *link) return link; } if (PHASH_hash(ph, link->key) != hash) { - return NULL; + return nullptr; } } @@ -462,7 +456,7 @@ static PEdge *p_wheel_edge_next(PEdge *e) static PEdge *p_wheel_edge_prev(PEdge *e) { - return (e->pair) ? e->pair->next : NULL; + return (e->pair) ? e->pair->next : nullptr; } static PEdge *p_boundary_edge_next(PEdge *e) @@ -670,9 +664,9 @@ static PVert *p_vert_lookup(ParamHandle *handle, PHashKey key, const float co[3] return p_vert_add(handle, key, co, e); } -static PVert *p_vert_copy(PChart *chart, PVert *v) +static PVert *p_vert_copy(ParamHandle *handle, PVert *v) { - PVert *nv = (PVert *)BLI_memarena_alloc(chart->handle->arena, sizeof(*nv)); + PVert *nv = (PVert *)BLI_memarena_alloc(handle->arena, sizeof(*nv)); copy_v3_v3(nv->co, v->co); nv->uv[0] = v->uv[0]; @@ -700,7 +694,7 @@ static PEdge *p_edge_lookup(ParamHandle *handle, const PHashKey *vkeys) e = (PEdge *)phash_next(handle->hash_edges, key, (PHashLink *)e); } - return NULL; + return nullptr; } static int p_face_exists(ParamHandle *handle, const ParamKey *pvkeys, int i1, int i2, int i3) @@ -727,20 +721,6 @@ static int p_face_exists(ParamHandle *handle, const ParamKey *pvkeys, int i1, in return false; } -static PChart *p_chart_new(ParamHandle *handle) -{ - PChart *chart = (PChart *)MEM_callocN(sizeof(*chart), "PChart"); - chart->handle = handle; - - return chart; -} - -static void p_chart_delete(PChart *chart) -{ - /* the actual links are free by memarena */ - MEM_freeN(chart); -} - static bool p_edge_implicit_seam(PEdge *e, PEdge *ep) { float *uv1, *uv2, *uvp1, *uvp2; @@ -789,7 +769,7 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv key = PHASH_edge(key1, key2); pe = (PEdge *)phash_lookup(handle->hash_edges, key); - *r_pair = NULL; + *r_pair = nullptr; while (pe) { if (pe != e) { @@ -802,7 +782,7 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv /* don't connect seams and t-junctions */ if ((pe->flag & PEDGE_SEAM) || *r_pair || (topology_from_uvs && p_edge_implicit_seam(e, pe))) { - *r_pair = NULL; + *r_pair = nullptr; return false; } @@ -816,12 +796,12 @@ static bool p_edge_has_pair(ParamHandle *handle, PEdge *e, bool topology_from_uv if (*r_pair && (e->vert == (*r_pair)->vert)) { if ((*r_pair)->next->pair || (*r_pair)->next->next->pair) { /* non unfoldable, maybe mobius ring or klein bottle */ - *r_pair = NULL; + *r_pair = nullptr; return false; } } - return (*r_pair != NULL); + return (*r_pair != nullptr); } static bool p_edge_connect_pair(ParamHandle *handle, @@ -829,7 +809,7 @@ static bool p_edge_connect_pair(ParamHandle *handle, bool topology_from_uvs, PEdge ***stack) { - PEdge *pair = NULL; + PEdge *pair = nullptr; if (!e->pair && p_edge_has_pair(handle, e, topology_from_uvs, &pair)) { if (e->vert == pair->vert) { @@ -845,13 +825,13 @@ static bool p_edge_connect_pair(ParamHandle *handle, } } - return (e->pair != NULL); + return (e->pair != nullptr); } static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs) { - PEdge **stackbase = MEM_mallocN(sizeof(*stackbase) * phash_size(handle->hash_faces), - "Pstackbase"); + PEdge **stackbase = (PEdge **)MEM_mallocN(sizeof(*stackbase) * phash_size(handle->hash_faces), + "Pstackbase"); PEdge **stack = stackbase; PFace *f, *first; PEdge *e, *e1, *e2; @@ -898,14 +878,14 @@ static int p_connect_pairs(ParamHandle *handle, bool topology_from_uvs) return ncharts; } -static void p_split_vert(PChart *chart, PEdge *e) +static void p_split_vert(ParamHandle *handle, PChart *chart, PEdge *e) { - PEdge *we, *lastwe = NULL; + PEdge *we, *lastwe = nullptr; PVert *v = e->vert; bool copy = true; if (e->flag & PEDGE_PIN) { - chart->flag |= PCHART_HAS_PINS; + chart->has_pins = true; } if (e->flag & PEDGE_VERTEX_SPLIT) { @@ -938,7 +918,7 @@ static void p_split_vert(PChart *chart, PEdge *e) if (copy) { /* not found, copying */ v->flag |= PVERT_SPLIT; - v = p_vert_copy(chart, v); + v = p_vert_copy(handle, v); v->flag |= PVERT_SPLIT; v->nextlink = chart->verts; @@ -957,20 +937,18 @@ static void p_split_vert(PChart *chart, PEdge *e) static PChart **p_split_charts(ParamHandle *handle, PChart *chart, int ncharts) { - PChart **charts = MEM_mallocN(sizeof(*charts) * ncharts, "PCharts"), *nchart; - PFace *f, *nextf; - int i; + PChart **charts = (PChart **)MEM_callocN(sizeof(*charts) * ncharts, "PCharts"); - for (i = 0; i < ncharts; i++) { - charts[i] = p_chart_new(handle); + for (int i = 0; i < ncharts; i++) { + charts[i] = (PChart *)MEM_callocN(sizeof(*chart), "PChart"); } - f = chart->faces; + PFace *f = chart->faces; while (f) { PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; - nextf = f->nextlink; + PFace *nextf = f->nextlink; - nchart = charts[f->u.chart]; + PChart *nchart = charts[f->u.chart]; f->nextlink = nchart->faces; nchart->faces = f; @@ -984,9 +962,9 @@ static PChart **p_split_charts(ParamHandle *handle, PChart *chart, int ncharts) nchart->nfaces++; nchart->nedges += 3; - p_split_vert(nchart, e1); - p_split_vert(nchart, e2); - p_split_vert(nchart, e3); + p_split_vert(handle, nchart, e1); + p_split_vert(handle, nchart, e2); + p_split_vert(handle, nchart, e3); f = nextf; } @@ -1015,9 +993,9 @@ static PFace *p_face_add(ParamHandle *handle) e2->next = e3; e3->next = e1; - e1->pair = NULL; - e2->pair = NULL; - e3->pair = NULL; + e1->pair = nullptr; + e2->pair = nullptr; + e3->pair = nullptr; e1->flag = 0; e2->flag = 0; @@ -1086,16 +1064,16 @@ static PFace *p_face_add_construct(ParamHandle *handle, return f; } -static PFace *p_face_add_fill(PChart *chart, PVert *v1, PVert *v2, PVert *v3) +static PFace *p_face_add_fill(ParamHandle *handle, PChart *chart, PVert *v1, PVert *v2, PVert *v3) { - PFace *f = p_face_add(chart->handle); + PFace *f = p_face_add(handle); PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; e1->vert = v1; e2->vert = v2; e3->vert = v3; - e1->orig_uv = e2->orig_uv = e3->orig_uv = NULL; + e1->orig_uv = e2->orig_uv = e3->orig_uv = nullptr; f->nextlink = chart->faces; chart->faces = f; @@ -1147,7 +1125,7 @@ static void p_chart_boundaries(PChart *chart, PEdge **r_outer) chart->nboundaries = 0; if (r_outer) { - *r_outer = NULL; + *r_outer = nullptr; } for (e = chart->edges; e; e = e->nextlink) { @@ -1202,7 +1180,7 @@ static float p_edge_boundary_angle(PEdge *e) return angle; } -static void p_chart_fill_boundary(PChart *chart, PEdge *be, int nedges) +static void p_chart_fill_boundary(ParamHandle *handle, PChart *chart, PEdge *be, int nedges) { PEdge *e, *e1, *e2; @@ -1238,12 +1216,12 @@ static void p_chart_fill_boundary(PChart *chart, PEdge *be, int nedges) BLI_heap_remove(heap, e1->u.heaplink); BLI_heap_remove(heap, e2->u.heaplink); - e->u.heaplink = e1->u.heaplink = e2->u.heaplink = NULL; + e->u.heaplink = e1->u.heaplink = e2->u.heaplink = nullptr; e->flag |= PEDGE_FILLED; e1->flag |= PEDGE_FILLED; - f = p_face_add_fill(chart, e->vert, e1->vert, e2->vert); + f = p_face_add_fill(handle, chart, e->vert, e1->vert, e2->vert); f->flag |= PFACE_FILLED; ne = f->edge->next->next; @@ -1276,10 +1254,10 @@ static void p_chart_fill_boundary(PChart *chart, PEdge *be, int nedges) } } - BLI_heap_free(heap, NULL); + BLI_heap_free(heap, nullptr); } -static void p_chart_fill_boundaries(PChart *chart, PEdge *outer) +static void p_chart_fill_boundaries(ParamHandle *handle, PChart *chart, PEdge *outer) { PEdge *e, *be; /* *enext - as yet unused */ int nedges; @@ -1300,7 +1278,7 @@ static void p_chart_fill_boundaries(PChart *chart, PEdge *outer) } while (be != e); if (e != outer) { - p_chart_fill_boundary(chart, e, nedges); + p_chart_fill_boundary(handle, chart, e, nedges); } } } @@ -1559,7 +1537,7 @@ static void p_vert_harmonic_insert(PVert *v) e = p_wheel_edge_next(e); } while (e && (e != v->edge)); - if (e == NULL) { + if (e == nullptr) { npoints++; } @@ -1573,7 +1551,7 @@ static void p_vert_harmonic_insert(PVert *v) points[i][0] = e->next->vert->uv[0]; points[i][1] = e->next->vert->uv[1]; - if (nexte == NULL) { + if (nexte == nullptr) { i++; points[i][0] = e->next->next->vert->uv[0]; points[i][1] = e->next->next->vert->uv[1]; @@ -1917,8 +1895,8 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair) int nshapeold = 0, nshapenew = 0; p_collapsing_verts(edge, pair, &oldv, &keepv); - oldf1 = (edge) ? edge->face : NULL; - oldf2 = (pair) ? pair->face : NULL; + oldf1 = (edge) ? edge->face : nullptr; + oldf2 = (pair) ? pair->face : nullptr; sub_v3_v3v3(edgevec, keepv->co, oldv->co); @@ -1939,7 +1917,7 @@ static float p_collapse_cost(PEdge *edge, PEdge *pair) # if 0 shapecost += dot_v3v3(co1, keepv->co); - if (p_wheel_edge_next(e) == NULL) { + if (p_wheel_edge_next(e) == nullptr) { shapecost += dot_v3v3(co2, keepv->co); } # endif @@ -1992,14 +1970,14 @@ static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine { PEdge *e, *enext, *pair; - *r_mine = NULL; + *r_mine = nullptr; *r_mincost = 0.0f; e = vert->edge; do { if (p_collapse_allowed(e, e->pair)) { float cost = p_collapse_cost(e, e->pair); - if ((*r_mine == NULL) || (cost < *r_mincost)) { + if ((*r_mine == nullptr) || (cost < *r_mincost)) { *r_mincost = cost; *r_mine = e; } @@ -2007,14 +1985,14 @@ static void p_collapse_cost_vertex(PVert *vert, float *r_mincost, PEdge **r_mine enext = p_wheel_edge_next(e); - if (enext == NULL) { + if (enext == nullptr) { /* The other boundary edge, where we only have the pair half-edge. */ pair = e->next->next; - if (p_collapse_allowed(NULL, pair)) { - float cost = p_collapse_cost(NULL, pair); + if (p_collapse_allowed(nullptr, pair)) { + float cost = p_collapse_cost(nullptr, pair); - if ((*r_mine == NULL) || (cost < *r_mincost)) { + if ((*r_mine == nullptr) || (cost < *r_mincost)) { *r_mincost = cost; *r_mine = pair; } @@ -2031,13 +2009,13 @@ static void p_chart_post_collapse_flush(PChart *chart, PEdge *collapsed) { /* Move to `collapsed_*`. */ - PVert *v, *nextv = NULL, *verts = chart->verts; - PEdge *e, *nexte = NULL, *edges = chart->edges, *laste = NULL; - PFace *f, *nextf = NULL, *faces = chart->faces; + PVert *v, *nextv = nullptr, *verts = chart->verts; + PEdge *e, *nexte = nullptr, *edges = chart->edges, *laste = nullptr; + PFace *f, *nextf = nullptr, *faces = chart->faces; - chart->verts = chart->collapsed_verts = NULL; - chart->edges = chart->collapsed_edges = NULL; - chart->faces = chart->collapsed_faces = NULL; + chart->verts = chart->collapsed_verts = nullptr; + chart->edges = chart->collapsed_edges = nullptr; + chart->faces = chart->collapsed_faces = nullptr; chart->nverts = chart->nedges = chart->nfaces = 0; @@ -2101,9 +2079,9 @@ static void p_chart_post_split_flush(PChart *chart) { /* Move from `collapsed_*`. */ - PVert *v, *nextv = NULL; - PEdge *e, *nexte = NULL; - PFace *f, *nextf = NULL; + PVert *v, *nextv = nullptr; + PEdge *e, *nexte = nullptr; + PFace *f, *nextf = nullptr; for (v = chart->collapsed_verts; v; v = nextv) { nextv = v->nextlink; @@ -2126,9 +2104,9 @@ static void p_chart_post_split_flush(PChart *chart) chart->nfaces++; } - chart->collapsed_verts = NULL; - chart->collapsed_edges = NULL; - chart->collapsed_faces = NULL; + chart->collapsed_verts = nullptr; + chart->collapsed_edges = nullptr; + chart->collapsed_faces = nullptr; } static void p_chart_simplify_compute(PChart *chart) @@ -2140,7 +2118,7 @@ static void p_chart_simplify_compute(PChart *chart) Heap *heap = BLI_heap_new(); PVert *v, **wheelverts; - PEdge *collapsededges = NULL, *e; + PEdge *collapsededges = nullptr, *e; int nwheelverts, i, ncollapsed = 0; wheelverts = MEM_mallocN(sizeof(PVert *) * chart->nverts, "PChartWheelVerts"); @@ -2148,7 +2126,7 @@ static void p_chart_simplify_compute(PChart *chart) /* insert all potential collapses into heap */ for (v = chart->verts; v; v = v->nextlink) { float cost; - PEdge *e = NULL; + PEdge *e = nullptr; p_collapse_cost_vertex(v, &cost, &e); @@ -2156,12 +2134,12 @@ static void p_chart_simplify_compute(PChart *chart) v->u.heaplink = BLI_heap_insert(heap, cost, e); } else { - v->u.heaplink = NULL; + v->u.heaplink = nullptr; } } for (e = chart->edges; e; e = e->nextlink) { - e->u.nextcollapse = NULL; + e->u.nextcollapse = nullptr; } /* pop edge collapse out of heap one by one */ @@ -2181,12 +2159,12 @@ static void p_chart_simplify_compute(PChart *chart) if (edge->vert->u.heaplink != link) { edge->flag |= (PEDGE_COLLAPSE_EDGE | PEDGE_COLLAPSE_PAIR); - edge->next->vert->u.heaplink = NULL; + edge->next->vert->u.heaplink = nullptr; SWAP(PEdge *, edge, pair); } else { edge->flag |= PEDGE_COLLAPSE_EDGE; - edge->vert->u.heaplink = NULL; + edge->vert->u.heaplink = nullptr; } p_collapsing_verts(edge, pair, &oldv, &keepv); @@ -2199,7 +2177,7 @@ static void p_chart_simplify_compute(PChart *chart) wheelverts[nwheelverts++] = wheele->next->vert; nexte = p_wheel_edge_next(wheele); - if (nexte == NULL) { + if (nexte == nullptr) { wheelverts[nwheelverts++] = wheele->next->next->vert; } @@ -2211,13 +2189,13 @@ static void p_chart_simplify_compute(PChart *chart) for (i = 0; i < nwheelverts; i++) { float cost; - PEdge *collapse = NULL; + PEdge *collapse = nullptr; v = wheelverts[i]; if (v->u.heaplink) { BLI_heap_remove(heap, v->u.heaplink); - v->u.heaplink = NULL; + v->u.heaplink = nullptr; } p_collapse_cost_vertex(v, &cost, &collapse); @@ -2231,7 +2209,7 @@ static void p_chart_simplify_compute(PChart *chart) } MEM_freeN(wheelverts); - BLI_heap_free(heap, NULL); + BLI_heap_free(heap, nullptr); p_chart_post_collapse_flush(chart, collapsededges); } @@ -2282,14 +2260,14 @@ static void p_chart_simplify(PChart *chart) #define ABF_MAX_ITER 20 -typedef struct PAbfSystem { +using PAbfSystem = struct PAbfSystem { int ninterior, nfaces, nangles; float *alpha, *beta, *sine, *cosine, *weight; float *bAlpha, *bTriangle, *bInterior; float *lambdaTriangle, *lambdaPlanar, *lambdaLength; float (*J2dt)[3], *bstar, *dstar; float minangle, maxangle; -} PAbfSystem; +}; static void p_abf_setup_system(PAbfSystem *sys) { @@ -2309,7 +2287,7 @@ static void p_abf_setup_system(PAbfSystem *sys) sys->lambdaPlanar = (float *)MEM_callocN(sizeof(float) * sys->ninterior, "ABFlamdaplane"); sys->lambdaLength = (float *)MEM_mallocN(sizeof(float) * sys->ninterior, "ABFlambdalen"); - sys->J2dt = MEM_mallocN(sizeof(float) * sys->nangles * 3, "ABFj2dt"); + sys->J2dt = static_cast<float(*)[3]>(MEM_mallocN(sizeof(float) * sys->nangles * 3, "ABFj2dt")); sys->bstar = (float *)MEM_mallocN(sizeof(float) * sys->nfaces, "ABFbstar"); sys->dstar = (float *)MEM_mallocN(sizeof(float) * sys->nfaces, "ABFdstar"); @@ -2808,7 +2786,7 @@ static bool p_chart_abf_solve(PChart *chart) } } - chart->u.lscm.abf_alpha = MEM_dupallocN(sys.alpha); + chart->u.lscm.abf_alpha = (float *)MEM_dupallocN(sys.alpha); p_abf_free_system(&sys); return true; @@ -2869,8 +2847,8 @@ static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2) static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVert **pin2) { - PEdge *be, *lastbe = NULL, *maxe1 = NULL, *maxe2 = NULL, *be1, *be2; - PEdge *cure = NULL, *firste1 = NULL, *firste2 = NULL, *nextbe; + PEdge *be, *lastbe = nullptr, *maxe1 = nullptr, *maxe2 = nullptr, *be1, *be2; + PEdge *cure = nullptr, *firste1 = nullptr, *firste2 = nullptr, *nextbe; float maxlen = 0.0f, curlen = 0.0f, totlen = 0.0f, firstlen = 0.0f; float len1, len2; @@ -2909,7 +2887,7 @@ static bool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVe } curlen = 0.0f; - cure = NULL; + cure = nullptr; } lastbe = be; @@ -2984,8 +2962,8 @@ static void p_chart_extrema_verts(PChart *chart, PVert **pin1, PVert **pin2) minv[0] = minv[1] = minv[2] = 1e20; maxv[0] = maxv[1] = maxv[2] = -1e20; - minvert[0] = minvert[1] = minvert[2] = NULL; - maxvert[0] = maxvert[1] = maxvert[2] = NULL; + minvert[0] = minvert[1] = minvert[2] = nullptr; + maxvert[0] = maxvert[1] = maxvert[2] = nullptr; for (v = chart->verts; v; v = v->nextlink) { for (i = 0; i < 3; i++) { @@ -3049,7 +3027,7 @@ static void p_chart_lscm_begin(PChart *chart, bool live, bool abf) } if ((live && (!select || !deselect))) { - chart->u.lscm.context = NULL; + chart->u.lscm.context = nullptr; } else { #if 0 @@ -3266,16 +3244,14 @@ static void p_chart_lscm_transform_single_pin(PChart *chart) static void p_chart_lscm_end(PChart *chart) { - if (chart->u.lscm.context) { - EIG_linear_solver_delete(chart->u.lscm.context); - } + EIG_linear_solver_delete(chart->u.lscm.context); + chart->u.lscm.context = nullptr; MEM_SAFE_FREE(chart->u.lscm.abf_alpha); - chart->u.lscm.context = NULL; - chart->u.lscm.pin1 = NULL; - chart->u.lscm.pin2 = NULL; - chart->u.lscm.single_pin = NULL; + chart->u.lscm.pin1 = nullptr; + chart->u.lscm.pin2 = nullptr; + chart->u.lscm.single_pin = nullptr; chart->u.lscm.single_pin_area = 0.0f; } @@ -3288,7 +3264,7 @@ static void p_stretch_pin_boundary(PChart *chart) PVert *v; for (v = chart->verts; v; v = v->nextlink) { - if (v->edge->pair == NULL) { + if (v->edge->pair == nullptr) { v->flag |= PVERT_PIN; } else { @@ -3566,7 +3542,7 @@ static float p_chart_minimum_area_angle(PChart *chart) } /* find left/top/right/bottom points, and compute angle for each point */ - angles = MEM_mallocN(sizeof(float) * npoints, "PMinAreaAngles"); + angles = (float *)MEM_mallocN(sizeof(float) * npoints, "PMinAreaAngles"); i_min = i_max = 0; miny = 1e10; @@ -3690,7 +3666,8 @@ static void p_chart_rotate_minimum_area(PChart *chart) static void p_chart_rotate_fit_aabb(PChart *chart) { - float(*points)[2] = MEM_mallocN(sizeof(*points) * chart->nverts, __func__); + float(*points)[2] = static_cast<float(*)[2]>( + MEM_mallocN(sizeof(*points) * chart->nverts, __func__)); p_chart_uv_to_array(chart, points); @@ -3707,10 +3684,10 @@ static void p_chart_rotate_fit_aabb(PChart *chart) /* Exported */ -ParamHandle *GEO_uv_parametrizer_construct_begin(void) +ParamHandle *GEO_uv_parametrizer_construct_begin() { - ParamHandle *handle = MEM_callocN(sizeof(*handle), "ParamHandle"); - handle->construction_chart = p_chart_new(handle); + ParamHandle *handle = new ParamHandle(); + handle->construction_chart = (PChart *)MEM_callocN(sizeof(PChart), "PChart"); handle->state = PHANDLE_STATE_ALLOCATED; handle->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "param construct arena"); handle->polyfill_arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "param polyfill arena"); @@ -3733,40 +3710,45 @@ void GEO_uv_parametrizer_aspect_ratio(ParamHandle *phandle, float aspx, float as void GEO_uv_parametrizer_delete(ParamHandle *phandle) { - int i; - + if (!phandle) { + return; + } param_assert(ELEM(phandle->state, PHANDLE_STATE_ALLOCATED, PHANDLE_STATE_CONSTRUCTED)); - for (i = 0; i < phandle->ncharts; i++) { - p_chart_delete(phandle->charts[i]); + for (int i = 0; i < phandle->ncharts; i++) { + MEM_SAFE_FREE(phandle->charts[i]); } MEM_SAFE_FREE(phandle->charts); if (phandle->pin_hash) { - BLI_ghash_free(phandle->pin_hash, NULL, NULL); - phandle->pin_hash = NULL; + BLI_ghash_free(phandle->pin_hash, nullptr, nullptr); + phandle->pin_hash = nullptr; } - if (phandle->construction_chart) { - p_chart_delete(phandle->construction_chart); + MEM_SAFE_FREE(phandle->construction_chart); - phash_delete(phandle->hash_verts); - phash_delete(phandle->hash_edges); - phash_delete(phandle->hash_faces); - } + phash_delete(phandle->hash_verts); + phash_delete(phandle->hash_edges); + phash_delete(phandle->hash_faces); BLI_memarena_free(phandle->arena); BLI_memarena_free(phandle->polyfill_arena); - BLI_heap_free(phandle->polyfill_heap, NULL); - MEM_freeN(phandle); + BLI_heap_free(phandle->polyfill_heap, nullptr); + + if (phandle->rng) { + BLI_rng_free(phandle->rng); + phandle->rng = nullptr; + } + + delete phandle; } -typedef struct GeoUVPinIndex { +using GeoUVPinIndex = struct GeoUVPinIndex { struct GeoUVPinIndex *next; float uv[2]; ParamKey reindex; -} GeoUVPinIndex; +}; /* Find a (mostly) unique ParamKey given a BMVert index and UV co-ordinates. * For each unique pinned UVs, return a unique ParamKey, starting with @@ -3782,7 +3764,8 @@ ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const return bmvertindex; /* No verts pinned. */ } - GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, POINTER_FROM_INT(bmvertindex)); + const GeoUVPinIndex *pinuvlist = (const GeoUVPinIndex *)BLI_ghash_lookup( + handle->pin_hash, POINTER_FROM_INT(bmvertindex)); if (!pinuvlist) { return bmvertindex; /* Vert not pinned. */ } @@ -3804,8 +3787,8 @@ ParamKey GEO_uv_find_pin_index(ParamHandle *handle, const int bmvertindex, const static GeoUVPinIndex *new_geo_uv_pinindex(ParamHandle *handle, const float uv[2]) { - GeoUVPinIndex *pinuv = BLI_memarena_alloc(handle->arena, sizeof(*pinuv)); - pinuv->next = NULL; + GeoUVPinIndex *pinuv = (GeoUVPinIndex *)BLI_memarena_alloc(handle->arena, sizeof(*pinuv)); + pinuv->next = nullptr; copy_v2_v2(pinuv->uv, uv); pinuv->reindex = PARAM_KEY_MAX - (handle->unique_pin_count++); return pinuv; @@ -3817,7 +3800,8 @@ void GEO_uv_prepare_pin_index(ParamHandle *handle, const int bmvertindex, const handle->pin_hash = BLI_ghash_int_new("uv pin reindex"); } - GeoUVPinIndex *pinuvlist = BLI_ghash_lookup(handle->pin_hash, POINTER_FROM_INT(bmvertindex)); + GeoUVPinIndex *pinuvlist = (GeoUVPinIndex *)BLI_ghash_lookup(handle->pin_hash, + POINTER_FROM_INT(bmvertindex)); if (!pinuvlist) { BLI_ghash_insert( handle->pin_hash, POINTER_FROM_INT(bmvertindex), new_geo_uv_pinindex(handle, uv)); @@ -3849,8 +3833,10 @@ static void p_add_ngon(ParamHandle *handle, MemArena *arena = handle->polyfill_arena; Heap *heap = handle->polyfill_heap; uint nfilltri = nverts - 2; - uint(*tris)[3] = BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri); - float(*projverts)[2] = BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts); + uint(*tris)[3] = static_cast<uint(*)[3]>( + BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri)); + float(*projverts)[2] = static_cast<float(*)[2]>( + BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts)); /* Calc normal, flipped: to get a positive 2d cross product. */ float normal[3]; @@ -3879,7 +3865,7 @@ static void p_add_ngon(ParamHandle *handle, BLI_polyfill_beautify(projverts, nverts, tris, arena, heap); /* Add triangles. */ - for (int j = 0; j < nfilltri; j++) { + for (uint j = 0; j < nfilltri; j++) { uint *tri = tris[j]; uint v0 = tri[0]; uint v1 = tri[1]; @@ -3906,7 +3892,7 @@ void GEO_uv_parametrizer_face_add(ParamHandle *phandle, const bool *pin, const bool *select) { - param_assert(phash_lookup(phandle->hash_faces, key) == NULL); + param_assert(phash_lookup(phandle->hash_faces, key) == nullptr); param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); param_assert(ELEM(nverts, 3, 4)); @@ -3957,13 +3943,13 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle, phandle->ncharts = p_connect_pairs(phandle, topology_from_uvs); phandle->charts = p_split_charts(phandle, chart, phandle->ncharts); - p_chart_delete(phandle->construction_chart); - phandle->construction_chart = NULL; + MEM_freeN(phandle->construction_chart); + phandle->construction_chart = nullptr; phash_delete(phandle->hash_verts); phash_delete(phandle->hash_edges); phash_delete(phandle->hash_faces); - phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = NULL; + phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = nullptr; for (i = j = 0; i < phandle->ncharts; i++) { PVert *v; @@ -3972,8 +3958,8 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle, p_chart_boundaries(chart, &outer); if (!topology_from_uvs && chart->nboundaries == 0) { - p_chart_delete(chart); - if (count_fail != NULL) { + MEM_freeN(chart); + if (count_fail != nullptr) { *count_fail += 1; } continue; @@ -3983,7 +3969,7 @@ void GEO_uv_parametrizer_construct_end(ParamHandle *phandle, j++; if (fill && (chart->nboundaries > 1)) { - p_chart_fill_boundaries(chart, outer); + p_chart_fill_boundaries(phandle, chart, outer); } for (v = chart->verts; v; v = v->nextlink) { @@ -4025,7 +4011,7 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, in if (chart->u.lscm.context) { const bool result = p_chart_lscm_solve(phandle, chart); - if (result && !(chart->flag & PCHART_HAS_PINS)) { + if (result && !chart->has_pins) { p_chart_rotate_minimum_area(chart); } else if (result && chart->u.lscm.single_pin) { @@ -4033,17 +4019,17 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, in p_chart_lscm_transform_single_pin(chart); } - if (!result || !(chart->flag & PCHART_HAS_PINS)) { + if (!result || !chart->has_pins) { p_chart_lscm_end(chart); } if (result) { - if (count_changed != NULL) { + if (count_changed != nullptr) { *count_changed += 1; } } else { - if (count_failed != NULL) { + if (count_failed != nullptr) { *count_failed += 1; } } @@ -4053,11 +4039,9 @@ void GEO_uv_parametrizer_lscm_solve(ParamHandle *phandle, int *count_changed, in void GEO_uv_parametrizer_lscm_end(ParamHandle *phandle) { - int i; + BLI_assert(phandle->state == PHANDLE_STATE_LSCM); - param_assert(phandle->state == PHANDLE_STATE_LSCM); - - for (i = 0; i < phandle->ncharts; i++) { + for (int i = 0; i < phandle->ncharts; i++) { p_chart_lscm_end(phandle->charts[i]); #if 0 p_chart_complexify(phandle->charts[i]); @@ -4119,9 +4103,6 @@ void GEO_uv_parametrizer_stretch_end(ParamHandle *phandle) { param_assert(phandle->state == PHANDLE_STATE_STRETCH); phandle->state = PHANDLE_STATE_CONSTRUCTED; - - BLI_rng_free(phandle->rng); - phandle->rng = NULL; } /* don't pack, just rotate (used for better packing) */ @@ -4133,7 +4114,7 @@ static void GEO_uv_parametrizer_pack_rotate(ParamHandle *phandle, bool ignore_pi for (i = 0; i < phandle->ncharts; i++) { chart = phandle->charts[i]; - if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { + if (ignore_pinned && chart->has_pins) { continue; } @@ -4169,12 +4150,12 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, } /* we may not use all these boxes */ - boxarray = MEM_mallocN(handle->ncharts * sizeof(BoxPack), "BoxPack box"); + boxarray = (BoxPack *)MEM_mallocN(handle->ncharts * sizeof(BoxPack), "BoxPack box"); for (i = 0; i < handle->ncharts; i++) { chart = handle->charts[i]; - if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { + if (ignore_pinned && chart->has_pins) { unpacked++; continue; } @@ -4190,7 +4171,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, box->w = chart->u.pack.size[0] + trans[0]; box->h = chart->u.pack.size[1] + trans[1]; - box->index = i; /* warning this index skips PCHART_HAS_PINS boxes */ + box->index = i; /* Warning this index skips chart->has_pins boxes. */ if (margin > 0.0f) { area += (double)sqrtf(box->w * box->h); @@ -4207,7 +4188,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, for (i = 0; i < handle->ncharts; i++) { chart = handle->charts[i]; - if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { + if (ignore_pinned && chart->has_pins) { unpacked++; continue; } @@ -4264,7 +4245,7 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, for (i = 0; i < phandle->ncharts; i++) { chart = phandle->charts[i]; - if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { + if (ignore_pinned && chart->has_pins) { continue; } @@ -4367,7 +4348,7 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, for (i = 0; i < phandle->ncharts; i++) { chart = phandle->charts[i]; - if (ignore_pinned && (chart->flag & PCHART_HAS_PINS)) { + if (ignore_pinned && chart->has_pins) { continue; } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index 5033e67d52e..68a4b39a21e 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -270,7 +270,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, } static void segment_list_item(struct uiList *UNUSED(ui_list), - struct bContext *UNUSED(C), + const struct bContext *UNUSED(C), struct uiLayout *layout, struct PointerRNA *UNUSED(idataptr), struct PointerRNA *itemptr, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c index f492e9ee044..74b7efb1d04 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilshrinkwrap.c @@ -202,7 +202,6 @@ static void updateDepsgraph(GpencilModifierData *md, CustomData_MeshMasks mask = {0}; if (BKE_shrinkwrap_needs_normals(mmd->shrink_type, mmd->shrink_mode)) { - mask.vmask |= CD_MASK_NORMAL; mask.lmask |= CD_MASK_NORMAL | CD_MASK_CUSTOMLOOPNORMAL; } @@ -225,7 +224,7 @@ static void updateDepsgraph(GpencilModifierData *md, ctx->node, &mmd->aux_target->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY); } } - DEG_add_modifier_to_transform_relation(ctx->node, "Shrinkwrap Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Shrinkwrap Modifier"); } static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 224146d0032..63433113822 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -366,8 +366,9 @@ typedef struct LineartData { /* Keep an copy of these data so when line art is running it's self-contained. */ bool cam_is_persp; - bool cam_is_persp_secondary; /* "Secondary" ones are from viewing camera (as opposed to shadow - camera), during shadow calculation. */ + /* "Secondary" ones are from viewing camera + * (as opposed to shadow camera), during shadow calculation. */ + bool cam_is_persp_secondary; float cam_obmat[4][4]; float cam_obmat_secondary[4][4]; double camera_pos[3]; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index a6b9f1420f1..0fd1d8ff51d 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -5227,7 +5227,7 @@ static void lineart_gpencil_generate(LineartCache *cache, } if (shaodow_selection) { if (ec->shadow_mask_bits != LRT_SHADOW_MASK_UNDEFINED) { - /* TODO(Yiming): Give a behaviour option for how to display undefined shadow info. */ + /* TODO(Yiming): Give a behavior option for how to display undefined shadow info. */ if ((shaodow_selection == LRT_SHADOW_FILTER_ILLUMINATED && (!(ec->shadow_mask_bits & LRT_SHADOW_MASK_ILLUMINATED)))) { continue; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c index 24762ce921d..bf42677d79c 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_shadow.c @@ -482,7 +482,7 @@ static void lineart_shadow_create_shadow_edge_array(LineartData *ld, * This process is repeated on each existing segments of the shadow edge (#e), which ensures they * all have been tested for closest segments after cutting. And in the diagram it's clear that the * left/right side of cuts are likely to be discontinuous, each cut's left side designates the - * right side of the last segment, and vise versa. */ + * right side of the last segment, and vice-versa. */ static void lineart_shadow_edge_cut(LineartData *ld, LineartShadowEdge *e, double start, diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 5e97909a2b8..1d67b5be4fb 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -190,8 +190,8 @@ set(OPENGL_SRC set(METAL_SRC metal/mtl_backend.mm - metal/mtl_context.mm metal/mtl_command_buffer.mm + metal/mtl_context.mm metal/mtl_debug.mm metal/mtl_framebuffer.mm metal/mtl_memory.mm @@ -323,6 +323,45 @@ set(GLSL_SRC shaders/common/gpu_shader_common_math_utils.glsl shaders/common/gpu_shader_common_mix_rgb.glsl + shaders/compositor/compositor_alpha_crop.glsl + shaders/compositor/compositor_box_mask.glsl + shaders/compositor/compositor_convert.glsl + shaders/compositor/compositor_ellipse_mask.glsl + shaders/compositor/compositor_flip.glsl + shaders/compositor/compositor_image_crop.glsl + shaders/compositor/compositor_projector_lens_distortion.glsl + shaders/compositor/compositor_realize_on_domain.glsl + shaders/compositor/compositor_screen_lens_distortion.glsl + shaders/compositor/compositor_set_alpha.glsl + shaders/compositor/compositor_split_viewer.glsl + + shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl + shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl + shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl + shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl + shaders/compositor/library/gpu_shader_compositor_color_balance.glsl + shaders/compositor/library/gpu_shader_compositor_color_correction.glsl + shaders/compositor/library/gpu_shader_compositor_color_matte.glsl + shaders/compositor/library/gpu_shader_compositor_color_spill.glsl + shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl + shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl + shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl + shaders/compositor/library/gpu_shader_compositor_exposure.glsl + shaders/compositor/library/gpu_shader_compositor_gamma.glsl + shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl + shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl + shaders/compositor/library/gpu_shader_compositor_invert.glsl + shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl + shaders/compositor/library/gpu_shader_compositor_main.glsl + shaders/compositor/library/gpu_shader_compositor_map_value.glsl + shaders/compositor/library/gpu_shader_compositor_normal.glsl + shaders/compositor/library/gpu_shader_compositor_posterize.glsl + shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl + shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl + shaders/compositor/library/gpu_shader_compositor_store_output.glsl + shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl + shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl + shaders/material/gpu_shader_material_add_shader.glsl shaders/material/gpu_shader_material_ambient_occlusion.glsl shaders/material/gpu_shader_material_anisotropic.glsl @@ -454,8 +493,11 @@ list(APPEND INC ${CMAKE_CURRENT_BINARY_DIR}) set(SRC_SHADER_CREATE_INFOS ../draw/engines/basic/shaders/infos/basic_depth_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_depth_of_field_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_film_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_light_culling_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_material_info.hh + ../draw/engines/eevee_next/shaders/infos/eevee_motion_blur_info.hh ../draw/engines/eevee_next/shaders/infos/eevee_velocity_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_info.hh ../draw/engines/gpencil/shaders/infos/gpencil_vfx_info.hh @@ -468,8 +510,8 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/overlay/shaders/infos/overlay_grid_info.hh ../draw/engines/overlay/shaders/infos/overlay_outline_info.hh ../draw/engines/overlay/shaders/infos/overlay_paint_info.hh - ../draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh ../draw/engines/overlay/shaders/infos/overlay_sculpt_curves_info.hh + ../draw/engines/overlay/shaders/infos/overlay_sculpt_info.hh ../draw/engines/overlay/shaders/infos/overlay_volume_info.hh ../draw/engines/overlay/shaders/infos/overlay_wireframe_info.hh ../draw/engines/select/shaders/infos/select_id_info.hh @@ -484,6 +526,7 @@ set(SRC_SHADER_CREATE_INFOS ../draw/engines/workbench/shaders/infos/workbench_transparent_resolve_info.hh ../draw/engines/workbench/shaders/infos/workbench_volume_info.hh ../draw/engines/image/shaders/infos/engine_image_info.hh + ../draw/intern/shaders/draw_debug_info.hh ../draw/intern/shaders/draw_fullscreen_info.hh ../draw/intern/shaders/draw_hair_refine_info.hh ../draw/intern/shaders/draw_object_infos_info.hh @@ -524,6 +567,18 @@ set(SRC_SHADER_CREATE_INFOS shaders/infos/gpu_shader_simple_lighting_info.hh shaders/infos/gpu_shader_text_info.hh shaders/infos/gpu_srgb_to_framebuffer_space_info.hh + + shaders/compositor/infos/compositor_alpha_crop_info.hh + shaders/compositor/infos/compositor_box_mask_info.hh + shaders/compositor/infos/compositor_convert_info.hh + shaders/compositor/infos/compositor_ellipse_mask_info.hh + shaders/compositor/infos/compositor_flip_info.hh + shaders/compositor/infos/compositor_image_crop_info.hh + shaders/compositor/infos/compositor_projector_lens_distortion_info.hh + shaders/compositor/infos/compositor_realize_on_domain_info.hh + shaders/compositor/infos/compositor_screen_lens_distortion_info.hh + shaders/compositor/infos/compositor_set_alpha_info.hh + shaders/compositor/infos/compositor_split_viewer_info.hh ) set(SHADER_CREATE_INFOS_CONTENT "") diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 7fad8dd23be..c085b592a77 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -14,6 +14,7 @@ #include "GPU_index_buffer.h" #include "GPU_shader.h" +#include "GPU_storage_buffer.h" #include "GPU_uniform_buffer.h" #include "GPU_vertex_buffer.h" @@ -92,8 +93,10 @@ void GPU_batch_init_ex(GPUBatch *batch, */ void GPU_batch_copy(GPUBatch *batch_dst, GPUBatch *batch_src); -#define GPU_batch_create(prim, verts, elem) GPU_batch_create_ex(prim, verts, elem, 0) -#define GPU_batch_init(batch, prim, verts, elem) GPU_batch_init_ex(batch, prim, verts, elem, 0) +#define GPU_batch_create(prim, verts, elem) \ + GPU_batch_create_ex(prim, verts, elem, (eGPUBatchFlag)0) +#define GPU_batch_init(batch, prim, verts, elem) \ + GPU_batch_init_ex(batch, prim, verts, elem, (eGPUBatchFlag)0) /** * Same as discard but does not free. (does not call free callback). @@ -171,7 +174,13 @@ void GPU_batch_draw_instanced(GPUBatch *batch, int i_count); /** * This does not bind/unbind shader and does not call GPU_matrix_bind(). */ -void GPU_batch_draw_advanced(GPUBatch *, int v_first, int v_count, int i_first, int i_count); +void GPU_batch_draw_advanced(GPUBatch *batch, int v_first, int v_count, int i_first, int i_count); + +/** + * Issue a draw call using GPU computed arguments. The argument are expected to be valid for the + * type of geometry drawn (index or non-indexed). + */ +void GPU_batch_draw_indirect(GPUBatch *batch, GPUStorageBuf *indirect_buf); #if 0 /* future plans */ diff --git a/source/blender/gpu/GPU_buffers.h b/source/blender/gpu/GPU_buffers.h index 89473ac0fe0..d1d91cb7508 100644 --- a/source/blender/gpu/GPU_buffers.h +++ b/source/blender/gpu/GPU_buffers.h @@ -22,6 +22,7 @@ struct CCGKey; struct DMFlagMat; struct GSet; struct TableGSet; +struct Mesh; struct MLoop; struct MLoopCol; struct MLoopTri; @@ -46,14 +47,11 @@ typedef struct GPU_PBVH_Buffers GPU_PBVH_Buffers; * * Threaded: do not call any functions that use OpenGL calls! */ -GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const struct MPoly *mpoly, - const struct MLoop *mloop, +GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const struct Mesh *mesh, const struct MLoopTri *looptri, - const struct MVert *mvert, - const int *face_indices, const int *sculpt_face_sets, - int face_indices_len, - const struct Mesh *mesh); + const int *face_indices, + int face_indices_len); /** * Threaded: do not call any functions that use OpenGL calls! @@ -91,9 +89,8 @@ enum { */ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, GPU_PBVH_Buffers *buffers, + const struct Mesh *mesh, const struct MVert *mvert, - const CustomData *vdata, - const CustomData *ldata, const float *vmask, const int *sculpt_face_sets, const int face_sets_color_seed, diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 3ca465fa57a..1ab06f3369d 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -121,6 +121,7 @@ typedef struct GPUCodegenOutput { char *surface; char *volume; char *thickness; + char *composite; char *material_functions; GPUShaderCreateInfo *create_info; @@ -166,10 +167,6 @@ bool GPU_stack_link(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out, ...); -GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, - struct bNode *node, - struct GPUNodeStack *stack, - int index); void GPU_material_output_surface(GPUMaterial *material, GPUNodeLink *link); void GPU_material_output_volume(GPUMaterial *material, GPUNodeLink *link); @@ -178,6 +175,8 @@ void GPU_material_output_thickness(GPUMaterial *material, GPUNodeLink *link); void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash); +void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link); + /** * Wrap a part of the material graph into a function. You need then need to call the function by * using something like #GPU_differentiate_float_function. @@ -218,6 +217,7 @@ GPUMaterial *GPU_material_from_nodetree(struct Scene *scene, void *thunk); void GPU_material_compile(GPUMaterial *mat); +void GPU_material_free_single(GPUMaterial *material); void GPU_material_free(struct ListBase *gpumaterial); void GPU_material_acquire(GPUMaterial *mat); @@ -319,6 +319,16 @@ struct GHash *GPU_uniform_attr_list_hash_new(const char *info); void GPU_uniform_attr_list_copy(GPUUniformAttrList *dest, GPUUniformAttrList *src); void GPU_uniform_attr_list_free(GPUUniformAttrList *set); +/* A callback passed to GPU_material_from_callbacks to construct the material graph by adding and + * linking the necessary GPU material nodes. */ +typedef void (*ConstructGPUMaterialFn)(void *thunk, GPUMaterial *material); + +/* Construct a GPU material from a set of callbacks. See the callback types for more information. + * The given thunk will be passed as the first parameter of each callback. */ +GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_function_cb, + GPUCodegenCallbackFn generate_code_function_cb, + void *thunk); + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 3460d33fe68..529a3da3ab9 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -148,11 +148,19 @@ typedef enum { GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ } GPUUniformBlockBuiltin; +typedef enum { + GPU_STORAGE_BUFFER_DEBUG_VERTS = 0, /* drw_debug_verts_buf */ + GPU_STORAGE_BUFFER_DEBUG_PRINT, /* drw_debug_print_buf */ + + GPU_NUM_STORAGE_BUFFERS, /* Special value, denotes number of builtin buffer blocks. */ +} GPUStorageBufferBuiltin; + void GPU_shader_set_srgb_uniform(GPUShader *shader); int GPU_shader_get_uniform(GPUShader *shader, const char *name); int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin); int GPU_shader_get_builtin_block(GPUShader *shader, int builtin); +int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin); /** DEPRECATED: Kept only because of Python GPU API. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name); int GPU_shader_get_ssbo(GPUShader *shader, const char *name); @@ -177,7 +185,9 @@ void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, fl void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]); void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]); void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]); +void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2]); void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]); +void GPU_shader_uniform_mat3_as_mat4(GPUShader *sh, const char *name, const float data[3][3]); void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]); void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]); diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 5bd20b7be98..6e31b1b69f6 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -331,6 +331,7 @@ int GPU_texture_orig_width(const GPUTexture *tex); int GPU_texture_orig_height(const GPUTexture *tex); void GPU_texture_orig_size_set(GPUTexture *tex, int w, int h); eGPUTextureFormat GPU_texture_format(const GPUTexture *tex); +const char *GPU_texture_format_description(eGPUTextureFormat texture_format); bool GPU_texture_array(const GPUTexture *tex); bool GPU_texture_cube(const GPUTexture *tex); bool GPU_texture_depth(const GPUTexture *tex); diff --git a/source/blender/gpu/intern/gpu_batch.cc b/source/blender/gpu/intern/gpu_batch.cc index 1b34b6e6c69..0b47a7b2952 100644 --- a/source/blender/gpu/intern/gpu_batch.cc +++ b/source/blender/gpu/intern/gpu_batch.cc @@ -270,6 +270,15 @@ void GPU_batch_draw_advanced( batch->draw(v_first, v_count, i_first, i_count); } +void GPU_batch_draw_indirect(GPUBatch *gpu_batch, GPUStorageBuf *indirect_buf) +{ + BLI_assert(Context::get()->shader != nullptr); + BLI_assert(indirect_buf != nullptr); + Batch *batch = static_cast<Batch *>(gpu_batch); + + batch->draw_indirect(indirect_buf); +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_batch_private.hh b/source/blender/gpu/intern/gpu_batch_private.hh index 23052f601d2..8ca19884fd7 100644 --- a/source/blender/gpu/intern/gpu_batch_private.hh +++ b/source/blender/gpu/intern/gpu_batch_private.hh @@ -29,6 +29,7 @@ class Batch : public GPUBatch { virtual ~Batch() = default; virtual void draw(int v_first, int v_count, int i_first, int i_count) = 0; + virtual void draw_indirect(GPUStorageBuf *indirect_buf) = 0; /* Convenience casts. */ IndexBuf *elem_() const diff --git a/source/blender/gpu/intern/gpu_buffers.c b/source/blender/gpu/intern/gpu_buffers.c index 14bbd82c282..d64b8b4118a 100644 --- a/source/blender/gpu/intern/gpu_buffers.c +++ b/source/blender/gpu/intern/gpu_buffers.c @@ -211,19 +211,18 @@ static void gpu_pbvh_batch_init(GPU_PBVH_Buffers *buffers, GPUPrimType prim) * \{ */ static bool gpu_pbvh_is_looptri_visible(const MLoopTri *lt, - const MVert *mvert, + const bool *hide_vert, const MLoop *mloop, const int *sculpt_face_sets) { - return (!paint_is_face_hidden(lt, mvert, mloop) && sculpt_face_sets && + return (!paint_is_face_hidden(lt, hide_vert, mloop) && sculpt_face_sets && sculpt_face_sets[lt->poly] > SCULPT_FACE_SET_NONE); } void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, GPU_PBVH_Buffers *buffers, + const Mesh *mesh, const MVert *mvert, - const CustomData *vdata, - const CustomData *ldata, const float *vmask, const int *sculpt_face_sets, int face_sets_color_seed, @@ -234,23 +233,23 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, GPUAttrRef vcol_refs[MAX_GPU_ATTR]; GPUAttrRef cd_uvs[MAX_GPU_ATTR]; - Mesh me_query; - BKE_id_attribute_copy_domains_temp(ID_ME, vdata, NULL, ldata, NULL, NULL, &me_query.id); + const bool *hide_vert = (bool *)CustomData_get_layer_named( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert"); - CustomDataLayer *actcol = BKE_id_attributes_active_color_get(&me_query.id); - eAttrDomain actcol_domain = actcol ? BKE_id_attribute_domain(&me_query.id, actcol) : + const CustomDataLayer *actcol = BKE_id_attributes_active_color_get(&mesh->id); + eAttrDomain actcol_domain = actcol ? BKE_id_attribute_domain(&mesh->id, actcol) : ATTR_DOMAIN_AUTO; - CustomDataLayer *rendercol = BKE_id_attributes_render_color_get(&me_query.id); + const CustomDataLayer *rendercol = BKE_id_attributes_render_color_get(&mesh->id); int totcol; if (update_flags & GPU_PBVH_BUFFERS_SHOW_VCOL) { totcol = gpu_pbvh_make_attr_offs(ATTR_DOMAIN_MASK_COLOR, CD_MASK_COLOR_ALL, - vdata, + &mesh->vdata, NULL, - ldata, + &mesh->ldata, NULL, vcol_refs, vbo_id->active_attrs_only, @@ -267,14 +266,14 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, CD_MASK_MLOOPUV, NULL, NULL, - ldata, + &mesh->ldata, NULL, cd_uvs, vbo_id->active_attrs_only, CD_MLOOPUV, ATTR_DOMAIN_CORNER, - get_active_layer(ldata, CD_MLOOPUV), - get_render_layer(ldata, CD_MLOOPUV)); + get_active_layer(&mesh->ldata, CD_MLOOPUV), + get_render_layer(&mesh->ldata, CD_MLOOPUV)); const bool show_mask = vmask && (update_flags & GPU_PBVH_BUFFERS_SHOW_MASK) != 0; const bool show_face_sets = sculpt_face_sets && @@ -308,13 +307,13 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, GPU_vertbuf_attr_get_raw_data(buffers->vert_buf, vbo_id->uv[uv_i], &uv_step); GPUAttrRef *ref = cd_uvs + uv_i; - CustomDataLayer *layer = ldata->layers + ref->layer_idx; + CustomDataLayer *layer = mesh->ldata.layers + ref->layer_idx; MLoopUV *muv = layer->data; for (uint i = 0; i < buffers->face_indices_len; i++) { const MLoopTri *lt = &buffers->looptri[buffers->face_indices[i]]; - if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { continue; } @@ -334,7 +333,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, MLoopCol *mcol = NULL; GPUAttrRef *ref = vcol_refs + col_i; - const CustomData *cdata = ref->domain == ATTR_DOMAIN_POINT ? vdata : ldata; + const CustomData *cdata = ref->domain == ATTR_DOMAIN_POINT ? &mesh->vdata : &mesh->ldata; CustomDataLayer *layer = cdata->layers + ref->layer_idx; bool color_loops = ref->domain == ATTR_DOMAIN_CORNER; @@ -354,7 +353,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, buffers->mloop[lt->tri[2]].v, }; - if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { continue; } @@ -394,7 +393,7 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, buffers->mloop[lt->tri[2]].v, }; - if (!gpu_pbvh_is_looptri_visible(lt, mvert, buffers->mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, buffers->mloop, sculpt_face_sets)) { continue; } @@ -457,21 +456,24 @@ void GPU_pbvh_mesh_buffers_update(PBVHGPUFormat *vbo_id, buffers->mvert = mvert; } -GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly, - const MLoop *mloop, +GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const Mesh *mesh, const MLoopTri *looptri, - const MVert *mvert, - const int *face_indices, const int *sculpt_face_sets, - const int face_indices_len, - const struct Mesh *mesh) + const int *face_indices, + const int face_indices_len) { GPU_PBVH_Buffers *buffers; int i, tottri; int tot_real_edges = 0; + const MPoly *mpoly = mesh->mpoly; + const MLoop *mloop = mesh->mloop; + buffers = MEM_callocN(sizeof(GPU_PBVH_Buffers), "GPU_Buffers"); + const bool *hide_vert = (bool *)CustomData_get_layer_named( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + /* smooth or flat for all */ buffers->smooth = mpoly[looptri[face_indices[0]].poly].flag & ME_SMOOTH; @@ -480,7 +482,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly, /* Count the number of visible triangles */ for (i = 0, tottri = 0; i < face_indices_len; i++) { const MLoopTri *lt = &looptri[face_indices[i]]; - if (gpu_pbvh_is_looptri_visible(lt, mvert, mloop, sculpt_face_sets)) { + if (gpu_pbvh_is_looptri_visible(lt, hide_vert, mloop, sculpt_face_sets)) { int r_edges[3]; BKE_mesh_looptri_get_real_edges(mesh, lt, r_edges); for (int j = 0; j < 3; j++) { @@ -513,7 +515,7 @@ GPU_PBVH_Buffers *GPU_pbvh_mesh_buffers_build(const MPoly *mpoly, const MLoopTri *lt = &looptri[face_indices[i]]; /* Skip hidden faces */ - if (!gpu_pbvh_is_looptri_visible(lt, mvert, mloop, sculpt_face_sets)) { + if (!gpu_pbvh_is_looptri_visible(lt, hide_vert, mloop, sculpt_face_sets)) { continue; } @@ -1246,9 +1248,7 @@ static int gpu_pbvh_make_attr_offs(eAttrDomainMask domain_mask, } } - /* ensure render layer is last - draw cache code seems to need this - */ + /* Ensure render layer is last, draw cache code seems to need this. */ for (int i = 0; i < count; i++) { GPUAttrRef *ref = r_cd_attrs + i; diff --git a/source/blender/gpu/intern/gpu_codegen.cc b/source/blender/gpu/intern/gpu_codegen.cc index 82441c3c89c..4a45a3e63ed 100644 --- a/source/blender/gpu/intern/gpu_codegen.cc +++ b/source/blender/gpu/intern/gpu_codegen.cc @@ -280,6 +280,7 @@ class GPUCodegen { void node_serialize(std::stringstream &eval_ss, const GPUNode *node); char *graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link); + char *graph_serialize(eGPUNodeTag tree_tag); static char *extract_c_str(std::stringstream &stream) { @@ -500,6 +501,19 @@ char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag, GPUNodeLink *output_link return eval_c_str; } +char *GPUCodegen::graph_serialize(eGPUNodeTag tree_tag) +{ + std::stringstream eval_ss; + LISTBASE_FOREACH (GPUNode *, node, &graph.nodes) { + if (node->tag & tree_tag) { + node_serialize(eval_ss, node); + } + } + char *eval_c_str = extract_c_str(eval_ss); + BLI_hash_mm2a_add(&hm2a_, (uchar *)eval_c_str, eval_ss.str().size()); + return eval_c_str; +} + void GPUCodegen::generate_uniform_buffer() { /* Extract uniform inputs. */ @@ -539,6 +553,9 @@ void GPUCodegen::generate_graphs() output.volume = graph_serialize(GPU_NODE_TAG_VOLUME, graph.outlink_volume); output.displacement = graph_serialize(GPU_NODE_TAG_DISPLACEMENT, graph.outlink_displacement); output.thickness = graph_serialize(GPU_NODE_TAG_THICKNESS, graph.outlink_thickness); + if (!BLI_listbase_is_empty(&graph.outlink_compositor)) { + output.composite = graph_serialize(GPU_NODE_TAG_COMPOSITOR); + } if (!BLI_listbase_is_empty(&graph.material_functions)) { std::stringstream eval_ss; @@ -569,9 +586,10 @@ GPUPass *GPU_generate_pass(GPUMaterial *material, GPUCodegenCallbackFn finalize_source_cb, void *thunk) { - /* Prune the unused nodes and extract attributes before compiling so the - * generated VBOs are ready to accept the future shader. */ gpu_node_graph_prune_unused(graph); + + /* Extract attributes before compiling so the generated VBOs are ready to accept the future + * shader. */ gpu_node_graph_finalize_uniform_attrs(graph); GPUCodegen codegen(material, graph); diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 4d3ea3e0c99..a4842ef0e43 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -141,7 +141,7 @@ static void gpu_material_ramp_texture_build(GPUMaterial *mat) mat->coba_builder = NULL; } -static void gpu_material_free_single(GPUMaterial *material) +void GPU_material_free_single(GPUMaterial *material) { bool do_free = atomic_sub_and_fetch_uint32(&material->refcount, 1) == 0; if (!do_free) { @@ -173,7 +173,7 @@ void GPU_material_free(ListBase *gpumaterial) LISTBASE_FOREACH (LinkData *, link, gpumaterial) { GPUMaterial *material = link->data; DRW_deferred_shader_remove(material); - gpu_material_free_single(material); + GPU_material_free_single(material); } BLI_freelistN(gpumaterial); } @@ -538,6 +538,13 @@ void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, BLI_addtail(&material->graph.outlink_aovs, aov_link); } +void GPU_material_add_output_link_composite(GPUMaterial *material, GPUNodeLink *link) +{ + GPUNodeGraphOutputLink *compositor_link = MEM_callocN(sizeof(GPUNodeGraphOutputLink), __func__); + compositor_link->outlink = link; + BLI_addtail(&material->graph.outlink_compositor, compositor_link); +} + char *GPU_material_split_sub_function(GPUMaterial *material, eGPUType return_type, GPUNodeLink **link) @@ -721,7 +728,7 @@ void GPU_material_acquire(GPUMaterial *mat) void GPU_material_release(GPUMaterial *mat) { - gpu_material_free_single(mat); + GPU_material_free_single(mat); } void GPU_material_compile(GPUMaterial *mat) @@ -772,3 +779,42 @@ void GPU_materials_free(Main *bmain) // BKE_world_defaults_free_gpu(); BKE_material_defaults_free_gpu(); } + +GPUMaterial *GPU_material_from_callbacks(ConstructGPUMaterialFn construct_function_cb, + GPUCodegenCallbackFn generate_code_function_cb, + void *thunk) +{ + /* Allocate a new material and its material graph, and initialize its reference count. */ + GPUMaterial *material = MEM_callocN(sizeof(GPUMaterial), "GPUMaterial"); + material->graph.used_libraries = BLI_gset_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, "GPUNodeGraph.used_libraries"); + material->refcount = 1; + + /* Construct the material graph by adding and linking the necessary GPU material nodes. */ + construct_function_cb(thunk, material); + + /* Create and initialize the texture storing color bands used by Ramp and Curve nodes. */ + gpu_material_ramp_texture_build(material); + + /* Lookup an existing pass in the cache or generate a new one. */ + material->pass = GPU_generate_pass(material, &material->graph, generate_code_function_cb, thunk); + + /* The pass already exists in the pass cache but its shader already failed to compile. */ + if (material->pass == NULL) { + material->status = GPU_MAT_FAILED; + gpu_node_graph_free(&material->graph); + return material; + } + + /* The pass already exists in the pass cache and its shader is already compiled. */ + GPUShader *shader = GPU_pass_shader_get(material->pass); + if (shader != NULL) { + material->status = GPU_MAT_SUCCESS; + gpu_node_graph_free_nodes(&material->graph); + return material; + } + + /* The material was created successfully but still needs to be compiled. */ + material->status = GPU_MAT_CREATED; + return material; +} diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 684070dbdc0..377cbc53893 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -75,9 +75,26 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const eGPUType if (STR_ELEM(name, "set_value", "set_rgb", "set_rgba") && (input->type == type)) { input = MEM_dupallocN(outnode->inputs.first); + + switch (input->source) { + case GPU_SOURCE_ATTR: + input->attr->users++; + break; + case GPU_SOURCE_UNIFORM_ATTR: + input->uniform_attr->users++; + break; + case GPU_SOURCE_TEX: + case GPU_SOURCE_TEX_TILED_MAPPING: + input->texture->users++; + break; + default: + break; + } + if (input->link) { input->link->users++; } + BLI_addtail(&node->inputs, input); return; } @@ -179,35 +196,21 @@ static GPUNodeLink *gpu_uniformbuffer_link(GPUMaterial *mat, BLI_assert(socket != NULL); BLI_assert(socket->in_out == in_out); - if ((socket->flag & SOCK_HIDE_VALUE) == 0) { - GPUNodeLink *link; - switch (socket->type) { - case SOCK_FLOAT: { - bNodeSocketValueFloat *socket_data = socket->default_value; - link = GPU_uniform(&socket_data->value); - break; - } - case SOCK_VECTOR: { - bNodeSocketValueVector *socket_data = socket->default_value; - link = GPU_uniform(socket_data->value); - break; - } - case SOCK_RGBA: { - bNodeSocketValueRGBA *socket_data = socket->default_value; - link = GPU_uniform(socket_data->value); - break; - } - default: - return NULL; - break; - } + if (socket->flag & SOCK_HIDE_VALUE) { + return NULL; + } - if (in_out == SOCK_IN) { - GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link); - } - return link; + if (!ELEM(socket->type, SOCK_FLOAT, SOCK_VECTOR, SOCK_RGBA)) { + return NULL; } - return NULL; + + GPUNodeLink *link = GPU_uniform(stack->vec); + + if (in_out == SOCK_IN) { + GPU_link(mat, gpu_uniform_set_function_from_type(socket->type), link, &stack->link); + } + + return link; } static void gpu_node_input_socket( @@ -735,14 +738,6 @@ bool GPU_stack_link(GPUMaterial *material, return valid; } -GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat, - bNode *node, - GPUNodeStack *stack, - const int index) -{ - return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT); -} - /* Node Graph */ static void gpu_inputs_free(ListBase *inputs) @@ -803,6 +798,7 @@ void gpu_node_graph_free(GPUNodeGraph *graph) { BLI_freelistN(&graph->outlink_aovs); BLI_freelistN(&graph->material_functions); + BLI_freelistN(&graph->outlink_compositor); gpu_node_graph_free_nodes(graph); BLI_freelistN(&graph->textures); @@ -855,6 +851,9 @@ void gpu_node_graph_prune_unused(GPUNodeGraph *graph) LISTBASE_FOREACH (GPUNodeGraphFunctionLink *, funclink, &graph->material_functions) { gpu_nodes_tag(funclink->outlink, GPU_NODE_TAG_FUNCTION); } + LISTBASE_FOREACH (GPUNodeGraphOutputLink *, compositor_link, &graph->outlink_compositor) { + gpu_nodes_tag(compositor_link->outlink, GPU_NODE_TAG_COMPOSITOR); + } for (GPUNode *node = graph->nodes.first, *next = NULL; node; node = next) { next = node->next; diff --git a/source/blender/gpu/intern/gpu_node_graph.h b/source/blender/gpu/intern/gpu_node_graph.h index ae472d5b7aa..08ff8bbef58 100644 --- a/source/blender/gpu/intern/gpu_node_graph.h +++ b/source/blender/gpu/intern/gpu_node_graph.h @@ -59,6 +59,7 @@ typedef enum { GPU_NODE_TAG_THICKNESS = (1 << 3), GPU_NODE_TAG_AOV = (1 << 4), GPU_NODE_TAG_FUNCTION = (1 << 5), + GPU_NODE_TAG_COMPOSITOR = (1 << 6), } eGPUNodeTag; ENUM_OPERATORS(eGPUNodeTag, GPU_NODE_TAG_FUNCTION) @@ -158,6 +159,8 @@ typedef struct GPUNodeGraph { ListBase outlink_aovs; /* List of GPUNodeGraphFunctionLink */ ListBase material_functions; + /* List of GPUNodeGraphOutputLink */ + ListBase outlink_compositor; /* Requested attributes and textures. */ ListBase attributes; diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index fe9aacb95f9..08c768b28ba 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -7,6 +7,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_math_matrix.h" #include "BLI_string_utils.h" #include "GPU_capabilities.h" @@ -382,6 +383,8 @@ GPUShader *GPU_shader_create_from_info(const GPUShaderCreateInfo *_info) sources.append(resources.c_str()); sources.append(layout.c_str()); sources.extend(code); + sources.extend(info.dependencies_generated); + sources.append(info.compute_source_generated.c_str()); shader->compute_shader_from_glsl(sources); } @@ -575,6 +578,12 @@ int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +int GPU_shader_get_builtin_ssbo(GPUShader *shader, int builtin) +{ + ShaderInterface *interface = unwrap(shader)->interface; + return interface->ssbo_builtin((GPUStorageBufferBuiltin)builtin); +} + int GPU_shader_get_ssbo(GPUShader *shader, const char *name) { ShaderInterface *interface = unwrap(shader)->interface; @@ -702,12 +711,25 @@ void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4] GPU_shader_uniform_vector(sh, loc, 4, 1, data); } +void GPU_shader_uniform_2iv(GPUShader *sh, const char *name, const int data[2]) +{ + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector_int(sh, loc, 2, 1, data); +} + void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]) { const int loc = GPU_shader_get_uniform(sh, name); GPU_shader_uniform_vector(sh, loc, 16, 1, (const float *)data); } +void GPU_shader_uniform_mat3_as_mat4(GPUShader *sh, const char *name, const float data[3][3]) +{ + float matrix[4][4]; + copy_m4_m3(matrix, data); + GPU_shader_uniform_mat4(sh, name, matrix); +} + void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]) { const int loc = GPU_shader_get_uniform(sh, name); diff --git a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc index d8af2fc584d..575f98bf428 100644 --- a/source/blender/gpu/intern/gpu_shader_builder_stubs.cc +++ b/source/blender/gpu/intern/gpu_shader_builder_stubs.cc @@ -111,7 +111,7 @@ void BKE_id_attribute_copy_domains_temp(short UNUSED(id_type), const struct CustomData *UNUSED(ldata), const struct CustomData *UNUSED(pdata), const struct CustomData *UNUSED(cdata), - struct ID *UNUSED(i_id)) + struct ID *UNUSED(r_id)) { } @@ -137,7 +137,7 @@ eAttrDomain BKE_id_attribute_domain(const struct ID *UNUSED(id), /** \name Stubs of BKE_paint.h * \{ */ bool paint_is_face_hidden(const struct MLoopTri *UNUSED(lt), - const struct MVert *UNUSED(mvert), + const bool *UNUSED(hide_vert), const struct MLoop *UNUSED(mloop)) { BLI_assert_unreachable(); @@ -225,6 +225,13 @@ bool CustomData_has_layer(const struct CustomData *UNUSED(data), int UNUSED(type return false; } +void *CustomData_get_layer_named(const struct CustomData *UNUSED(data), + int UNUSED(type), + const char *UNUSED(name)) +{ + return nullptr; +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/gpu/intern/gpu_shader_create_info.cc b/source/blender/gpu/intern/gpu_shader_create_info.cc index bc0731862cb..110b77f1f52 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.cc +++ b/source/blender/gpu/intern/gpu_shader_create_info.cc @@ -306,6 +306,14 @@ void gpu_shader_create_info_init() info->builtins_ |= gpu_shader_dependency_get_builtins(info->fragment_source_); info->builtins_ |= gpu_shader_dependency_get_builtins(info->geometry_source_); info->builtins_ |= gpu_shader_dependency_get_builtins(info->compute_source_); + + /* Automatically amend the create info for ease of use of the debug feature. */ + if ((info->builtins_ & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) { + info->additional_info("draw_debug_draw"); + } + if ((info->builtins_ & BuiltinBits::USE_DEBUG_PRINT) == BuiltinBits::USE_DEBUG_PRINT) { + info->additional_info("draw_debug_print"); + } } } diff --git a/source/blender/gpu/intern/gpu_shader_create_info.hh b/source/blender/gpu/intern/gpu_shader_create_info.hh index 8e05412d0ee..82defc436e0 100644 --- a/source/blender/gpu/intern/gpu_shader_create_info.hh +++ b/source/blender/gpu/intern/gpu_shader_create_info.hh @@ -127,8 +127,12 @@ enum class BuiltinBits { VERTEX_ID = (1 << 14), WORK_GROUP_ID = (1 << 15), WORK_GROUP_SIZE = (1 << 16), + + /* Not a builtin but a flag we use to tag shaders that use the debug features. */ + USE_DEBUG_DRAW = (1 << 29), + USE_DEBUG_PRINT = (1 << 30), }; -ENUM_OPERATORS(BuiltinBits, BuiltinBits::WORK_GROUP_SIZE); +ENUM_OPERATORS(BuiltinBits, BuiltinBits::USE_DEBUG_PRINT); /** * Follow convention described in: @@ -298,6 +302,7 @@ struct ShaderCreateInfo { /** Manually set generated code. */ std::string vertex_source_generated = ""; std::string fragment_source_generated = ""; + std::string compute_source_generated = ""; std::string geometry_source_generated = ""; std::string typedef_source_generated = ""; /** Manually set generated dependencies. */ @@ -818,6 +823,7 @@ struct ShaderCreateInfo { TEST_EQUAL(*this, b, builtins_); TEST_EQUAL(*this, b, vertex_source_generated); TEST_EQUAL(*this, b, fragment_source_generated); + TEST_EQUAL(*this, b, compute_source_generated); TEST_EQUAL(*this, b, typedef_source_generated); TEST_VECTOR_EQUAL(*this, b, vertex_inputs_); TEST_EQUAL(*this, b, geometry_layout_); diff --git a/source/blender/gpu/intern/gpu_shader_dependency.cc b/source/blender/gpu/intern/gpu_shader_dependency.cc index d91e15243f3..2c59cb6e501 100644 --- a/source/blender/gpu/intern/gpu_shader_dependency.cc +++ b/source/blender/gpu/intern/gpu_shader_dependency.cc @@ -11,6 +11,7 @@ #include <algorithm> #include <iomanip> #include <iostream> +#include <regex> #include <sstream> #include "BLI_ghash.h" @@ -42,7 +43,7 @@ struct GPUSource { StringRefNull source; Vector<GPUSource *> dependencies; bool dependencies_init = false; - shader::BuiltinBits builtins = (shader::BuiltinBits)0; + shader::BuiltinBits builtins = shader::BuiltinBits::NONE; std::string processed_source; GPUSource(const char *path, @@ -54,46 +55,45 @@ struct GPUSource { /* Scan for builtins. */ /* FIXME: This can trigger false positive caused by disabled #if blocks. */ /* TODO(fclem): Could be made faster by scanning once. */ - if (source.find("gl_FragCoord", 0)) { + if (source.find("gl_FragCoord", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::FRAG_COORD; } - if (source.find("gl_FrontFacing", 0)) { + if (source.find("gl_FrontFacing", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::FRONT_FACING; } - if (source.find("gl_GlobalInvocationID", 0)) { + if (source.find("gl_GlobalInvocationID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::GLOBAL_INVOCATION_ID; } - if (source.find("gl_InstanceID", 0)) { + if (source.find("gl_InstanceID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::INSTANCE_ID; } - if (source.find("gl_LocalInvocationID", 0)) { + if (source.find("gl_LocalInvocationID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::LOCAL_INVOCATION_ID; } - if (source.find("gl_LocalInvocationIndex", 0)) { + if (source.find("gl_LocalInvocationIndex", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::LOCAL_INVOCATION_INDEX; } - if (source.find("gl_NumWorkGroup", 0)) { + if (source.find("gl_NumWorkGroup", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::NUM_WORK_GROUP; } - if (source.find("gl_PointCoord", 0)) { + if (source.find("gl_PointCoord", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::POINT_COORD; } - if (source.find("gl_PointSize", 0)) { + if (source.find("gl_PointSize", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::POINT_SIZE; } - if (source.find("gl_PrimitiveID", 0)) { + if (source.find("gl_PrimitiveID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::PRIMITIVE_ID; } - if (source.find("gl_VertexID", 0)) { + if (source.find("gl_VertexID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::VERTEX_ID; } - if (source.find("gl_WorkGroupID", 0)) { + if (source.find("gl_WorkGroupID", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::WORK_GROUP_ID; } - if (source.find("gl_WorkGroupSize", 0)) { + if (source.find("gl_WorkGroupSize", 0) != StringRef::not_found) { builtins |= shader::BuiltinBits::WORK_GROUP_SIZE; } - /* TODO(fclem): We could do that at compile time. */ /* Limit to shared header files to avoid the temptation to use C++ syntax in .glsl files. */ if (filename.endswith(".h") || filename.endswith(".hh")) { @@ -101,6 +101,18 @@ struct GPUSource { quote_preprocess(); } else { + if (source.find("'") != StringRef::not_found) { + char_literals_preprocess(); + } + if (source.find("drw_print") != StringRef::not_found) { + string_preprocess(); + } + if ((source.find("drw_debug_") != StringRef::not_found) && + /* Avoid these two files where it makes no sense to add the dependency. */ + (filename != "common_debug_draw_lib.glsl" && + filename != "draw_debug_draw_display_vert.glsl")) { + builtins |= shader::BuiltinBits::USE_DEBUG_DRAW; + } check_no_quotes(); } @@ -522,6 +534,217 @@ struct GPUSource { } } + void char_literals_preprocess() + { + const StringRefNull input = source; + std::stringstream output; + int64_t cursor = -1; + int64_t last_pos = 0; + + while (true) { + cursor = find_token(input, '\'', cursor + 1); + if (cursor == -1) { + break; + } + /* Output anything between 2 print statement. */ + output << input.substr(last_pos, cursor - last_pos); + + /* Extract string. */ + int64_t char_start = cursor + 1; + int64_t char_end = find_token(input, '\'', char_start); + CHECK(char_end, input, cursor, "Malformed char literal. Missing ending `'`."); + + StringRef input_char = input.substr(char_start, char_end - char_start); + if (input_char.size() == 0) { + CHECK(-1, input, cursor, "Malformed char literal. Empty character constant"); + } + + uint8_t char_value = input_char[0]; + + if (input_char[0] == '\\') { + if (input_char[1] == 'n') { + char_value = '\n'; + } + else { + CHECK(-1, input, cursor, "Unsupported escaped character"); + } + } + else { + if (input_char.size() > 1) { + CHECK(-1, input, cursor, "Malformed char literal. Multi-character character constant"); + } + } + + char hex[8]; + SNPRINTF(hex, "0x%.2Xu", char_value); + output << hex; + + cursor = last_pos = char_end + 1; + } + /* If nothing has been changed, do not allocate processed_source. */ + if (last_pos == 0) { + return; + } + + if (last_pos != 0) { + output << input.substr(last_pos); + } + processed_source = output.str(); + source = processed_source.c_str(); + } + + /* Replace print(string) by equivalent drw_print_char4() sequence. */ + void string_preprocess() + { + const StringRefNull input = source; + std::stringstream output; + int64_t cursor = -1; + int64_t last_pos = 0; + + while (true) { + cursor = find_keyword(input, "drw_print", cursor + 1); + if (cursor == -1) { + break; + } + + bool do_endl = false; + StringRef func = input.substr(cursor); + if (func.startswith("drw_print(")) { + do_endl = true; + } + else if (func.startswith("drw_print_no_endl(")) { + do_endl = false; + } + else { + continue; + } + + /* Output anything between 2 print statement. */ + output << input.substr(last_pos, cursor - last_pos); + + /* Extract string. */ + int64_t str_start = input.find('(', cursor) + 1; + int64_t semicolon = find_token(input, ';', str_start + 1); + CHECK(semicolon, input, cursor, "Malformed print(). Missing `;` ."); + int64_t str_end = rfind_token(input, ')', semicolon); + if (str_end < str_start) { + CHECK(-1, input, cursor, "Malformed print(). Missing closing `)` ."); + } + + std::stringstream sub_output; + StringRef input_args = input.substr(str_start, str_end - str_start); + + auto print_string = [&](std::string str) -> int { + size_t len_before_pad = str.length(); + /* Pad string to uint size. */ + while (str.length() % 4 != 0) { + str += " "; + } + /* Keep everything in one line to not mess with the shader logs. */ + sub_output << "/* " << str << "*/"; + sub_output << "drw_print_string_start(" << len_before_pad << ");"; + for (size_t i = 0; i < len_before_pad; i += 4) { + uint8_t chars[4] = {*(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 0), + *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 1), + *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 2), + *(reinterpret_cast<const uint8_t *>(str.c_str()) + i + 3)}; + if (i + 4 > len_before_pad) { + chars[len_before_pad - i] = '\0'; + } + char uint_hex[12]; + SNPRINTF(uint_hex, "0x%.2X%.2X%.2X%.2Xu", chars[3], chars[2], chars[1], chars[0]); + sub_output << "drw_print_char4(" << StringRefNull(uint_hex) << ");"; + } + return 0; + }; + + std::string func_args = input_args; + /* Workaround to support function call inside prints. We replace commas by a non control + * character `$` in order to use simpler regex later. */ + bool string_scope = false; + int func_scope = 0; + for (char &c : func_args) { + if (c == '"') { + string_scope = !string_scope; + } + else if (!string_scope) { + if (c == '(') { + func_scope++; + } + else if (c == ')') { + func_scope--; + } + else if (c == ',' && func_scope != 0) { + c = '$'; + } + } + } + + const bool print_as_variable = (input_args[0] != '"') && find_token(input_args, ',') == -1; + if (print_as_variable) { + /* Variable or expression debugging. */ + std::string arg = input_args; + /* Pad align most values. */ + while (arg.length() % 4 != 0) { + arg += " "; + } + print_string(arg); + print_string("= "); + sub_output << "drw_print_value(" << input_args << ");"; + } + else { + const std::regex arg_regex( + /* String args. */ + "[\\s]*\"([^\r\n\t\f\v\"]*)\"" + /* OR. */ + "|" + /* value args. */ + "([^,]+)"); + std::smatch args_match; + std::string::const_iterator args_search_start(func_args.cbegin()); + while (std::regex_search(args_search_start, func_args.cend(), args_match, arg_regex)) { + args_search_start = args_match.suffix().first; + std::string arg_string = args_match[1].str(); + std::string arg_val = args_match[2].str(); + + if (arg_string.empty()) { + for (char &c : arg_val) { + if (c == '$') { + c = ','; + } + } + sub_output << "drw_print_value(" << arg_val << ");"; + } + else { + print_string(arg_string); + } + } + } + + if (do_endl) { + sub_output << "drw_print_newline();"; + } + + output << sub_output.str(); + + cursor = last_pos = str_end + 1; + } + /* If nothing has been changed, do not allocate processed_source. */ + if (last_pos == 0) { + return; + } + + if (filename != "common_debug_print_lib.glsl") { + builtins |= shader::BuiltinBits::USE_DEBUG_PRINT; + } + + if (last_pos != 0) { + output << input.substr(last_pos); + } + processed_source = output.str(); + source = processed_source.c_str(); + } + #undef find_keyword #undef rfind_keyword #undef find_token @@ -537,6 +760,15 @@ struct GPUSource { this->dependencies_init = true; int64_t pos = -1; + using namespace shader; + /* Auto dependency injection for debug capabilities. */ + if ((builtins & BuiltinBits::USE_DEBUG_DRAW) == BuiltinBits::USE_DEBUG_DRAW) { + dependencies.append_non_duplicates(dict.lookup("common_debug_draw_lib.glsl")); + } + if ((builtins & BuiltinBits::USE_DEBUG_PRINT) == BuiltinBits::USE_DEBUG_PRINT) { + dependencies.append_non_duplicates(dict.lookup("common_debug_print_lib.glsl")); + } + while (true) { GPUSource *dependency_source = nullptr; @@ -558,6 +790,7 @@ struct GPUSource { return 1; } } + /* Recursive. */ int result = dependency_source->init_dependencies(dict, g_functions); if (result != 0) { @@ -583,7 +816,7 @@ struct GPUSource { shader::BuiltinBits builtins_get() const { - shader::BuiltinBits out_builtins = shader::BuiltinBits::NONE; + shader::BuiltinBits out_builtins = builtins; for (auto *dep : dependencies) { out_builtins |= dep->builtins; } @@ -593,7 +826,8 @@ struct GPUSource { bool is_from_material_library() const { return (filename.startswith("gpu_shader_material_") || - filename.startswith("gpu_shader_common_")) && + filename.startswith("gpu_shader_common_") || + filename.startswith("gpu_shader_compositor_")) && filename.endswith(".glsl"); } }; diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh index 60344757b43..812244c9b3a 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.hh +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -56,6 +56,7 @@ class ShaderInterface { /** Location of builtin uniforms. Fast access, no lookup needed. */ int32_t builtins_[GPU_NUM_UNIFORMS]; int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]; + int32_t builtin_buffers_[GPU_NUM_STORAGE_BUFFERS]; public: ShaderInterface(); @@ -116,9 +117,17 @@ class ShaderInterface { return builtin_blocks_[builtin]; } + /* Returns binding position. */ + inline int32_t ssbo_builtin(const GPUStorageBufferBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_STORAGE_BUFFERS); + return builtin_buffers_[builtin]; + } + protected: static inline const char *builtin_uniform_name(GPUUniformBuiltin u); static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u); + static inline const char *builtin_storage_block_name(GPUStorageBufferBuiltin u); inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const; inline void copy_input_name(ShaderInput *input, @@ -212,6 +221,18 @@ inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBu } } +inline const char *ShaderInterface::builtin_storage_block_name(GPUStorageBufferBuiltin u) +{ + switch (u) { + case GPU_STORAGE_BUFFER_DEBUG_VERTS: + return "drw_debug_verts_buf"; + case GPU_STORAGE_BUFFER_DEBUG_PRINT: + return "drw_debug_print_buf"; + default: + return nullptr; + } +} + /* Returns string length including '\0' terminator. */ inline uint32_t ShaderInterface::set_input_name(ShaderInput *input, char *name, diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 218d22ddf53..e52311b3bf0 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -641,6 +641,112 @@ eGPUTextureFormat GPU_texture_format(const GPUTexture *tex) return reinterpret_cast<const Texture *>(tex)->format_get(); } +const char *GPU_texture_format_description(eGPUTextureFormat texture_format) +{ + switch (texture_format) { + case GPU_RGBA8UI: + return "RGBA8UI"; + case GPU_RGBA8I: + return "RGBA8I"; + case GPU_RGBA8: + return "RGBA8"; + case GPU_RGBA32UI: + return "RGBA32UI"; + case GPU_RGBA32I: + return "RGBA32I"; + case GPU_RGBA32F: + return "RGBA32F"; + case GPU_RGBA16UI: + return "RGBA16UI"; + case GPU_RGBA16I: + return "RGBA16I"; + case GPU_RGBA16F: + return "RGBA16F"; + case GPU_RGBA16: + return "RGBA16"; + case GPU_RG8UI: + return "RG8UI"; + case GPU_RG8I: + return "RG8I"; + case GPU_RG8: + return "RG8"; + case GPU_RG32UI: + return "RG32UI"; + case GPU_RG32I: + return "RG32I"; + case GPU_RG32F: + return "RG32F"; + case GPU_RG16UI: + return "RG16UI"; + case GPU_RG16I: + return "RG16I"; + case GPU_RG16F: + return "RG16F"; + case GPU_RG16: + return "RG16"; + case GPU_R8UI: + return "R8UI"; + case GPU_R8I: + return "R8I"; + case GPU_R8: + return "R8"; + case GPU_R32UI: + return "R32UI"; + case GPU_R32I: + return "R32I"; + case GPU_R32F: + return "R32F"; + case GPU_R16UI: + return "R16UI"; + case GPU_R16I: + return "R16I"; + case GPU_R16F: + return "R16F"; + case GPU_R16: + return "R16"; + + /* Special formats texture & render-buffer. */ + case GPU_RGB10_A2: + return "RGB10A2"; + case GPU_R11F_G11F_B10F: + return "R11FG11FB10F"; + case GPU_DEPTH32F_STENCIL8: + return "DEPTH32FSTENCIL8"; + case GPU_DEPTH24_STENCIL8: + return "DEPTH24STENCIL8"; + case GPU_SRGB8_A8: + return "SRGB8A8"; + + /* Texture only format */ + case (GPU_RGB16F): + return "RGB16F"; + + /* Special formats texture only */ + case GPU_SRGB8_A8_DXT1: + return "SRGB8_A8_DXT1"; + case GPU_SRGB8_A8_DXT3: + return "SRGB8_A8_DXT3"; + case GPU_SRGB8_A8_DXT5: + return "SRGB8_A8_DXT5"; + case GPU_RGBA8_DXT1: + return "RGBA8_DXT1"; + case GPU_RGBA8_DXT3: + return "RGBA8_DXT3"; + case GPU_RGBA8_DXT5: + return "RGBA8_DXT5"; + + /* Depth Formats */ + case GPU_DEPTH_COMPONENT32F: + return "DEPTH32F"; + case GPU_DEPTH_COMPONENT24: + return "DEPTH24"; + case GPU_DEPTH_COMPONENT16: + return "DEPTH16"; + } + BLI_assert_unreachable(); + return ""; +} + bool GPU_texture_depth(const GPUTexture *tex) { return (reinterpret_cast<const Texture *>(tex)->format_flag_get() & GPU_FORMAT_DEPTH) != 0; diff --git a/source/blender/gpu/metal/mtl_query.mm b/source/blender/gpu/metal/mtl_query.mm index dfda0a8de7f..f574140531d 100644 --- a/source/blender/gpu/metal/mtl_query.mm +++ b/source/blender/gpu/metal/mtl_query.mm @@ -9,7 +9,7 @@ namespace blender::gpu { static const size_t VISIBILITY_COUNT_PER_BUFFER = 512; -/* defined in the documentation but not queryable programmatically: +/* Defined in the documentation but can't be queried programmatically: * https://developer.apple.com/documentation/metal/mtlvisibilityresultmode/mtlvisibilityresultmodeboolean?language=objc */ static const size_t VISIBILITY_RESULT_SIZE_IN_BYTES = 8; diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index e425b87afe8..8646d94e2fd 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -133,11 +133,11 @@ class GLBackend : public GPUBackend { dynamic_cast<GLStorageBuf *>(indirect_buf)->bind_as(GL_DISPATCH_INDIRECT_BUFFER); /* This barrier needs to be here as it only work on the currently bound indirect buffer. */ - glMemoryBarrier(GL_DRAW_INDIRECT_BUFFER); + glMemoryBarrier(GL_COMMAND_BARRIER_BIT); glDispatchComputeIndirect((GLintptr)0); /* Unbind. */ - glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); + glBindBuffer(GL_DISPATCH_INDIRECT_BUFFER, 0); } /* Render Frame Coordination */ diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc index fde2a53bb0f..4ec86b98cbe 100644 --- a/source/blender/gpu/opengl/gl_batch.cc +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -18,6 +18,7 @@ #include "gl_debug.hh" #include "gl_index_buffer.hh" #include "gl_primitive.hh" +#include "gl_storage_buffer.hh" #include "gl_vertex_array.hh" #include "gl_batch.hh" @@ -326,4 +327,27 @@ void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) } } +void GLBatch::draw_indirect(GPUStorageBuf *indirect_buf) +{ + GL_CHECK_RESOURCES("Batch"); + + this->bind(0); + + dynamic_cast<GLStorageBuf *>(unwrap(indirect_buf))->bind_as(GL_DRAW_INDIRECT_BUFFER); + /* This barrier needs to be here as it only work on the currently bound indirect buffer. */ + glMemoryBarrier(GL_COMMAND_BARRIER_BIT); + + GLenum gl_type = to_gl(prim_type); + if (elem) { + const GLIndexBuf *el = this->elem_(); + GLenum index_type = to_gl(el->index_type_); + glDrawElementsIndirect(gl_type, index_type, (GLvoid *)nullptr); + } + else { + glDrawArraysIndirect(gl_type, (GLvoid *)nullptr); + } + /* Unbind. */ + glBindBuffer(GL_DRAW_INDIRECT_BUFFER, 0); +} + /** \} */ diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh index 1a18572c683..bb53d9b31f1 100644 --- a/source/blender/gpu/opengl/gl_batch.hh +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -93,6 +93,7 @@ class GLBatch : public Batch { public: void draw(int v_first, int v_count, int i_first, int i_count) override; + void draw_indirect(GPUStorageBuf *indirect_buf) override; void bind(int i_first); /* Convenience getters. */ diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc index 1b3ab2941a8..4623a14dab3 100644 --- a/source/blender/gpu/opengl/gl_shader_interface.cc +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -318,6 +318,13 @@ GLShaderInterface::GLShaderInterface(GLuint program) builtin_blocks_[u] = (block != nullptr) ? block->binding : -1; } + /* Builtin Storage Buffers */ + for (int32_t u_int = 0; u_int < GPU_NUM_STORAGE_BUFFERS; u_int++) { + GPUStorageBufferBuiltin u = static_cast<GPUStorageBufferBuiltin>(u_int); + const ShaderInput *block = this->ssbo_get(builtin_storage_block_name(u)); + builtin_buffers_[u] = (block != nullptr) ? block->binding : -1; + } + MEM_freeN(uniforms_from_blocks); /* Resize name buffer to save some memory. */ @@ -481,6 +488,13 @@ GLShaderInterface::GLShaderInterface(GLuint program, const shader::ShaderCreateI builtin_blocks_[u] = (block != nullptr) ? block->binding : -1; } + /* Builtin Storage Buffers */ + for (int32_t u_int = 0; u_int < GPU_NUM_STORAGE_BUFFERS; u_int++) { + GPUStorageBufferBuiltin u = static_cast<GPUStorageBufferBuiltin>(u_int); + const ShaderInput *block = this->ssbo_get(builtin_storage_block_name(u)); + builtin_buffers_[u] = (block != nullptr) ? block->binding : -1; + } + this->sort_inputs(); // this->debug_print(); diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc index 8be4ac29af6..46422124112 100644 --- a/source/blender/gpu/opengl/gl_state.cc +++ b/source/blender/gpu/opengl/gl_state.cc @@ -563,14 +563,14 @@ void GLStateManager::image_bind(Texture *tex_, int unit) } images_[unit] = tex->tex_id_; formats_[unit] = to_gl_internal_format(tex->format_); - tex->is_bound_ = true; + tex->is_bound_image_ = true; dirty_image_binds_ |= 1ULL << unit; } void GLStateManager::image_unbind(Texture *tex_) { GLTexture *tex = static_cast<GLTexture *>(tex_); - if (!tex->is_bound_) { + if (!tex->is_bound_image_) { return; } @@ -581,7 +581,7 @@ void GLStateManager::image_unbind(Texture *tex_) dirty_image_binds_ |= 1ULL << i; } } - tex->is_bound_ = false; + tex->is_bound_image_ = false; } void GLStateManager::image_unbind_all() diff --git a/source/blender/gpu/opengl/gl_storage_buffer.cc b/source/blender/gpu/opengl/gl_storage_buffer.cc index 4592adc3a61..83a56edcf04 100644 --- a/source/blender/gpu/opengl/gl_storage_buffer.cc +++ b/source/blender/gpu/opengl/gl_storage_buffer.cc @@ -72,7 +72,7 @@ void GLStorageBuf::bind(int slot) if (slot >= GLContext::max_ssbo_binds) { fprintf( stderr, - "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.", + "Error: Trying to bind \"%s\" ssbo to slot %d which is above the reported limit of %d.\n", name_, slot, GLContext::max_ssbo_binds); diff --git a/source/blender/gpu/opengl/gl_texture.cc b/source/blender/gpu/opengl/gl_texture.cc index cfb3184c4a5..2ce205353a3 100644 --- a/source/blender/gpu/opengl/gl_texture.cc +++ b/source/blender/gpu/opengl/gl_texture.cc @@ -40,6 +40,7 @@ GLTexture::~GLTexture() if (ctx != nullptr && is_bound_) { /* This avoid errors when the texture is still inside the bound texture array. */ ctx->state_manager->texture_unbind(this); + ctx->state_manager->image_unbind(this); } GLContext::tex_free(tex_id_); } diff --git a/source/blender/gpu/opengl/gl_texture.hh b/source/blender/gpu/opengl/gl_texture.hh index aeb9fc0e6b7..22c21d360c7 100644 --- a/source/blender/gpu/opengl/gl_texture.hh +++ b/source/blender/gpu/opengl/gl_texture.hh @@ -37,6 +37,8 @@ class GLTexture : public Texture { /** True if this texture is bound to at least one texture unit. */ /* TODO(fclem): How do we ensure thread safety here? */ bool is_bound_ = false; + /** Same as is_bound_ but for image slots. */ + bool is_bound_image_ = false; /** True if pixels in the texture have been initialized. */ bool has_pixels_ = false; diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc index e58cea9de43..022fbcfdf29 100644 --- a/source/blender/gpu/opengl/gl_uniform_buffer.cc +++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc @@ -65,11 +65,12 @@ void GLUniformBuf::update(const void *data) void GLUniformBuf::bind(int slot) { if (slot >= GLContext::max_ubo_binds) { - fprintf(stderr, - "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.", - name_, - slot, - GLContext::max_ubo_binds); + fprintf( + stderr, + "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.\n", + name_, + slot, + GLContext::max_ubo_binds); return; } diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl index fe89985ae7f..33108d3a989 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_color_utils.glsl @@ -140,6 +140,84 @@ void hsl_to_rgb(vec4 hsl, out vec4 outcol) outcol = vec4((nr - 0.5) * chroma + l, (ng - 0.5) * chroma + l, (nb - 0.5) * chroma + l, hsl.w); } +/* ** YCCA to RGBA ** */ + +void ycca_to_rgba_itu_601(vec4 ycca, out vec4 color) +{ + ycca.xyz *= 255.0; + ycca.xyz -= vec3(16.0, 128.0, 128.0); + color.rgb = mat3(vec3(1.164), 0.0, -0.392, 2.017, 1.596, -0.813, 0.0) * ycca.xyz; + color.rgb /= 255.0; + color.a = ycca.a; +} + +void ycca_to_rgba_itu_709(vec4 ycca, out vec4 color) +{ + ycca.xyz *= 255.0; + ycca.xyz -= vec3(16.0, 128.0, 128.0); + color.rgb = mat3(vec3(1.164), 0.0, -0.213, 2.115, 1.793, -0.534, 0.0) * ycca.xyz; + color.rgb /= 255.0; + color.a = ycca.a; +} + +void ycca_to_rgba_jpeg(vec4 ycca, out vec4 color) +{ + ycca.xyz *= 255.0; + color.rgb = mat3(vec3(1.0), 0.0, -0.34414, 1.772, 1.402, -0.71414, 0.0) * ycca.xyz; + color.rgb += vec3(-179.456, 135.45984, -226.816); + color.rgb /= 255.0; + color.a = ycca.a; +} + +/* ** RGBA to YCCA ** */ + +void rgba_to_ycca_itu_601(vec4 rgba, out vec4 ycca) +{ + rgba.rgb *= 255.0; + ycca.xyz = mat3(0.257, -0.148, 0.439, 0.504, -0.291, -0.368, 0.098, 0.439, -0.071) * rgba.rgb; + ycca.xyz += vec3(16.0, 128.0, 128.0); + ycca.xyz /= 255.0; + ycca.a = rgba.a; +} + +void rgba_to_ycca_itu_709(vec4 rgba, out vec4 ycca) +{ + rgba.rgb *= 255.0; + ycca.xyz = mat3(0.183, -0.101, 0.439, 0.614, -0.338, -0.399, 0.062, 0.439, -0.040) * rgba.rgb; + ycca.xyz += vec3(16.0, 128.0, 128.0); + ycca.xyz /= 255.0; + ycca.a = rgba.a; +} + +void rgba_to_ycca_jpeg(vec4 rgba, out vec4 ycca) +{ + rgba.rgb *= 255.0; + ycca.xyz = mat3(0.299, -0.16874, 0.5, 0.587, -0.33126, -0.41869, 0.114, 0.5, -0.08131) * + rgba.rgb; + ycca.xyz += vec3(0.0, 128.0, 128.0); + ycca.xyz /= 255.0; + ycca.a = rgba.a; +} + +/* ** YUVA to RGBA ** */ + +void yuva_to_rgba_itu_709(vec4 yuva, out vec4 color) +{ + color.rgb = mat3(vec3(1.0), 0.0, -0.21482, 2.12798, 1.28033, -0.38059, 0.0) * yuva.xyz; + color.a = yuva.a; +} + +/* ** RGBA to YUVA ** */ + +void rgba_to_yuva_itu_709(vec4 rgba, out vec4 yuva) +{ + yuva.xyz = mat3(0.2126, -0.09991, 0.615, 0.7152, -0.33609, -0.55861, 0.0722, 0.436, -0.05639) * + rgba.rgb; + yuva.a = rgba.a; +} + +/* ** Alpha Handling ** */ + void color_alpha_clear(vec4 color, out vec4 result) { result = vec4(color.rgb, 1.0); @@ -147,15 +225,50 @@ void color_alpha_clear(vec4 color, out vec4 result) void color_alpha_premultiply(vec4 color, out vec4 result) { - result = vec4(color.rgb * color.a, 1.0); + result = vec4(color.rgb * color.a, color.a); } void color_alpha_unpremultiply(vec4 color, out vec4 result) { if (color.a == 0.0 || color.a == 1.0) { - result = vec4(color.rgb, 1.0); + result = color; } else { - result = vec4(color.rgb / color.a, 1.0); + result = vec4(color.rgb / color.a, color.a); + } +} + +float linear_rgb_to_srgb(float color) +{ + if (color < 0.0031308) { + return (color < 0.0) ? 0.0 : color * 12.92; + } + + return 1.055 * pow(color, 1.0 / 2.4) - 0.055; +} + +vec3 linear_rgb_to_srgb(vec3 color) +{ + return vec3( + linear_rgb_to_srgb(color.r), linear_rgb_to_srgb(color.g), linear_rgb_to_srgb(color.b)); +} + +float srgb_to_linear_rgb(float color) +{ + if (color < 0.04045) { + return (color < 0.0) ? 0.0 : color * (1.0 / 12.92); } + + return pow((color + 0.055) * (1.0 / 1.055), 2.4); +} + +vec3 srgb_to_linear_rgb(vec3 color) +{ + return vec3( + srgb_to_linear_rgb(color.r), srgb_to_linear_rgb(color.g), srgb_to_linear_rgb(color.b)); +} + +float get_luminance(vec3 color, vec3 luminance_coefficients) +{ + return dot(color, luminance_coefficients); } diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl index 8948ed77557..db8e114ec7a 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_curves.glsl @@ -95,6 +95,81 @@ void curves_combined_only(float factor, result = mix(color, result, factor); } +/* Contrary to standard tone curve implementations, the film-like implementation tries to preserve + * the hue of the colors as much as possible. To understand why this might be a problem, consider + * the violet color (0.5, 0.0, 1.0). If this color was to be evaluated at a power curve x^4, the + * color will be blue (0.0625, 0.0, 1.0). So the color changes and not just its luminosity, which + * is what film-like tone curves tries to avoid. + * + * First, the channels with the lowest and highest values are identified and evaluated at the + * curve. Then, the third channel---the median---is computed while maintaining the original hue of + * the color. To do that, we look at the equation for deriving the hue from RGB values. Assuming + * the maximum, minimum, and median channels are known, and ignoring the 1/3 period offset of the + * hue, the equation is: + * + * hue = (median - min) / (max - min) [1] + * + * Since we have the new values for the minimum and maximum after evaluating at the curve, we also + * have: + * + * hue = (new_median - new_min) / (new_max - new_min) [2] + * + * Since we want the hue to be equivalent, by equating [1] and [2] and rearranging: + * + * (new_median - new_min) / (new_max - new_min) = (median - min) / (max - min) + * new_median - new_min = (new_max - new_min) * (median - min) / (max - min) + * new_median = new_min + (new_max - new_min) * (median - min) / (max - min) + * new_median = new_min + (median - min) * ((new_max - new_min) / (max - min)) [QED] + * + * Which gives us the median color that preserves the hue. More intuitively, the median is computed + * such that the change in the distance from the median to the minimum is proportional to the + * change in the distance from the minimum to the maximum. Finally, each of the new minimum, + * maximum, and median values are written to the color channel that they were originally extracted + * from. */ +void curves_film_like(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); + + /* Find the maximum, minimum, and median of the color channels. */ + float minimum = min(balanced.r, min(balanced.g, balanced.b)); + float maximum = max(balanced.r, max(balanced.g, balanced.b)); + float median = max(min(balanced.r, balanced.g), min(balanced.b, max(balanced.r, balanced.g))); + + /* Evaluate alpha curve map at the maximum and minimum channels. The alpha curve is the Combined + * curve in the UI. */ + float min_parameter = NORMALIZE_PARAMETER(minimum, range_minimum, range_divider); + float max_parameter = NORMALIZE_PARAMETER(maximum, range_minimum, range_divider); + float new_min = texture(curve_map, vec2(min_parameter, layer)).a; + float new_max = texture(curve_map, vec2(max_parameter, layer)).a; + + /* Then, extrapolate if needed. */ + new_min = extrapolate_if_needed(min_parameter, new_min, start_slope, end_slope); + new_max = extrapolate_if_needed(max_parameter, new_max, start_slope, end_slope); + + /* Compute the new median using the ratio between the new and the original range. */ + float scaling_ratio = (new_max - new_min) / (maximum - minimum); + float new_median = new_min + (median - minimum) * scaling_ratio; + + /* Write each value to its original channel. */ + bvec3 channel_is_min = equal(balanced.rgb, vec3(minimum)); + vec3 median_or_min = mix(vec3(new_median), vec3(new_min), channel_is_min); + bvec3 channel_is_max = equal(balanced.rgb, vec3(maximum)); + result.rgb = mix(median_or_min, vec3(new_max), channel_is_max); + result.a = color.a; + + result = mix(color, result, clamp(factor, 0.0, 1.0)); +} + void curves_vector(vec3 vector, sampler1DArray curve_map, const float layer, diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl index 124654963fd..1ba22b4c5da 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_math_utils.glsl @@ -34,6 +34,17 @@ float compatible_pow(float x, float y) return pow(x, y); } +/* A version of pow that returns a fallback value if the computation is undefined. From the spec: + * The result is undefined if x < 0 or if x = 0 and y is less than or equal 0. */ +float fallback_pow(float x, float y, float fallback) +{ + if (x < 0.0 || (x == 0.0 && y <= 0.0)) { + return fallback; + } + + return pow(x, y); +} + float wrap(float a, float b, float c) { float range = b - c; @@ -114,8 +125,24 @@ void vector_copy(vec3 normal, out vec3 outnormal) outnormal = normal; } +vec3 fallback_pow(vec3 a, float b, vec3 fallback) +{ + return vec3(fallback_pow(a.x, b, fallback.x), + fallback_pow(a.y, b, fallback.y), + fallback_pow(a.z, b, fallback.z)); +} + /* Matirx Math */ +/* Return a 2D rotation matrix with the angle that the input 2D vector makes with the x axis. */ +mat2 vector_to_rotation_matrix(vec2 vector) +{ + vec2 normalized_vector = normalize(vector); + float cos_angle = normalized_vector.x; + float sin_angle = normalized_vector.y; + return mat2(cos_angle, sin_angle, -sin_angle, cos_angle); +} + mat3 euler_to_mat3(vec3 euler) { float cx = cos(euler.x); diff --git a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl index f9652f1150b..39f3c722dd2 100644 --- a/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl +++ b/source/blender/gpu/shaders/common/gpu_shader_common_mix_rgb.glsl @@ -2,28 +2,24 @@ void mix_blend(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, col2, fac); outcol.a = col1.a; } void mix_add(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, col1 + col2, fac); outcol.a = col1.a; } void mix_mult(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, col1 * col2, fac); outcol.a = col1.a; } void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = vec4(1.0) - (vec4(facm) + fac * (vec4(1.0) - col2)) * (vec4(1.0) - col1); @@ -32,7 +28,6 @@ void mix_screen(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = col1; @@ -61,14 +56,30 @@ void mix_overlay(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_sub(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, col1 - col2, fac); outcol.a = col1.a; } void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); + float facm = 1.0 - fac; + + outcol = vec4(vec3(0.0), col1.a); + + if (col2.r != 0.0) { + outcol.r = facm * col1.r + fac * col1.r / col2.r; + } + if (col2.g != 0.0) { + outcol.g = facm * col1.g + fac * col1.g / col2.g; + } + if (col2.b != 0.0) { + outcol.b = facm * col1.b + fac * col1.b / col2.b; + } +} + +/* A variant of mix_div that fallback to the first color upon zero division. */ +void mix_div_fallback(float fac, vec4 col1, vec4 col2, out vec4 outcol) +{ float facm = 1.0 - fac; outcol = col1; @@ -86,28 +97,24 @@ void mix_div(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_diff(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = mix(col1, abs(col1 - col2), fac); outcol.a = col1.a; } void mix_dark(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol.rgb = mix(col1.rgb, min(col1.rgb, col2.rgb), fac); outcol.a = col1.a; } void mix_light(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol.rgb = mix(col1.rgb, max(col1.rgb, col2.rgb), fac); outcol.a = col1.a; } void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); outcol = col1; if (outcol.r != 0.0) { @@ -150,7 +157,6 @@ void mix_dodge(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float tmp, facm = 1.0 - fac; outcol = col1; @@ -200,7 +206,6 @@ void mix_burn(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = col1; @@ -220,7 +225,6 @@ void mix_hue(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = col1; @@ -238,7 +242,6 @@ void mix_sat(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; vec4 hsv, hsv2; @@ -251,7 +254,6 @@ void mix_val(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; outcol = col1; @@ -272,22 +274,26 @@ void mix_color(float fac, vec4 col1, vec4 col2, out vec4 outcol) void mix_soft(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); float facm = 1.0 - fac; vec4 one = vec4(1.0); vec4 scr = one - (one - col2) * (one - col1); outcol = facm * col1 + fac * ((one - col1) * col2 * col1 + col1 * scr); + outcol.a = col1.a; } void mix_linear(float fac, vec4 col1, vec4 col2, out vec4 outcol) { - fac = clamp(fac, 0.0, 1.0); - outcol = col1 + fac * (2.0 * (col2 - vec4(0.5))); + outcol.a = col1.a; } -void clamp_color(vec3 vec, vec3 min, vec3 max, out vec3 out_vec) +void clamp_color(vec4 vec, const vec4 min, const vec4 max, out vec4 out_vec) { out_vec = clamp(vec, min, max); } + +void multiply_by_alpha(float factor, vec4 color, out float result) +{ + result = factor * color.a; +} diff --git a/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl b/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl new file mode 100644 index 00000000000..d55c8efd4c6 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_alpha_crop.glsl @@ -0,0 +1,11 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + /* The lower bound is inclusive and upper bound is exclusive. */ + bool is_inside = all(greaterThanEqual(texel, lower_bound)) && all(lessThan(texel, upper_bound)); + /* Write the pixel color if it is inside the cropping region, otherwise, write zero. */ + vec4 color = is_inside ? texture_load(input_tx, texel) : vec4(0.0); + imageStore(output_img, texel, color); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl new file mode 100644 index 00000000000..fad23f28fde --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_box_mask.glsl @@ -0,0 +1,27 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1)); + uv -= location; + uv.y *= float(domain_size.y) / float(domain_size.x); + uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv; + bool is_inside = all(lessThan(abs(uv), size)); + + float base_mask_value = texture_load(base_mask_tx, texel).x; + float value = texture_load(mask_value_tx, texel).x; + +#if defined(CMP_NODE_MASKTYPE_ADD) + float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value; +#elif defined(CMP_NODE_MASKTYPE_SUBTRACT) + float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value; +#elif defined(CMP_NODE_MASKTYPE_MULTIPLY) + float output_mask_value = is_inside ? base_mask_value * value : 0.0; +#elif defined(CMP_NODE_MASKTYPE_NOT) + float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value; +#endif + + imageStore(output_mask_img, texel, vec4(output_mask_value)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_convert.glsl b/source/blender/gpu/shaders/compositor/compositor_convert.glsl new file mode 100644 index 00000000000..044fb057ca5 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_convert.glsl @@ -0,0 +1,8 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 value = texture_load(input_tx, texel); + imageStore(output_img, texel, CONVERT_EXPRESSION(value)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl new file mode 100644 index 00000000000..28f725067e0 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_ellipse_mask.glsl @@ -0,0 +1,27 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + vec2 uv = vec2(texel) / vec2(domain_size - ivec2(1)); + uv -= location; + uv.y *= float(domain_size.y) / float(domain_size.x); + uv = mat2(cos_angle, -sin_angle, sin_angle, cos_angle) * uv; + bool is_inside = length(uv / radius) < 1.0; + + float base_mask_value = texture_load(base_mask_tx, texel).x; + float value = texture_load(mask_value_tx, texel).x; + +#if defined(CMP_NODE_MASKTYPE_ADD) + float output_mask_value = is_inside ? max(base_mask_value, value) : base_mask_value; +#elif defined(CMP_NODE_MASKTYPE_SUBTRACT) + float output_mask_value = is_inside ? clamp(base_mask_value - value, 0.0, 1.0) : base_mask_value; +#elif defined(CMP_NODE_MASKTYPE_MULTIPLY) + float output_mask_value = is_inside ? base_mask_value * value : 0.0; +#elif defined(CMP_NODE_MASKTYPE_NOT) + float output_mask_value = is_inside ? (base_mask_value > 0.0 ? 0.0 : value) : base_mask_value; +#endif + + imageStore(output_mask_img, texel, vec4(output_mask_value)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_flip.glsl b/source/blender/gpu/shaders/compositor/compositor_flip.glsl new file mode 100644 index 00000000000..919c454ee63 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_flip.glsl @@ -0,0 +1,15 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + ivec2 size = texture_size(input_tx); + ivec2 flipped_texel = texel; + if (flip_x) { + flipped_texel.x = size.x - texel.x - 1; + } + if (flip_y) { + flipped_texel.y = size.y - texel.y - 1; + } + imageStore(output_img, texel, texture_load(input_tx, flipped_texel)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl b/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl new file mode 100644 index 00000000000..f20e033dee4 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_image_crop.glsl @@ -0,0 +1,7 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + imageStore(output_img, texel, texture_load(input_tx, texel + lower_bound)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl b/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl new file mode 100644 index 00000000000..cf961b20b34 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_projector_lens_distortion.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* Get the normalized coordinates of the pixel centers. */ + vec2 normalized_texel = (vec2(texel) + vec2(0.5)) / vec2(texture_size(input_tx)); + + /* Sample the red and blue channels shifted by the dispersion amount. */ + const float red = texture(input_tx, normalized_texel + vec2(dispersion, 0.0)).r; + const float green = texture_load(input_tx, texel).g; + const float blue = texture(input_tx, normalized_texel - vec2(dispersion, 0.0)).b; + + imageStore(output_img, texel, vec4(red, green, blue, 1.0)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl b/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl new file mode 100644 index 00000000000..b2961d07219 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_realize_on_domain.glsl @@ -0,0 +1,25 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* First, transform the input image by transforming the domain coordinates with the inverse of + * input image's transformation. The inverse transformation is an affine matrix and thus the + * coordinates should be in homogeneous coordinates. */ + vec2 coordinates = (mat3(inverse_transformation) * vec3(texel, 1.0)).xy; + + /* Since an input image with an identity transformation is supposed to be centered in the domain, + * we subtract the offset between the lower left corners of the input image and the domain, which + * is half the difference between their sizes, because the difference in size is on both sides of + * the centered image. */ + ivec2 domain_size = imageSize(domain_img); + ivec2 input_size = texture_size(input_tx); + vec2 offset = (domain_size - input_size) / 2.0; + + /* Subtract the offset and divide by the input image size to get the relevant coordinates into + * the sampler's expected [0, 1] range. */ + vec2 normalized_coordinates = (coordinates - offset) / input_size; + + imageStore(domain_img, texel, texture(input_tx, normalized_coordinates)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl b/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl new file mode 100644 index 00000000000..dc572ea5aaf --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_screen_lens_distortion.glsl @@ -0,0 +1,151 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_hash.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +/* A model that approximates lens distortion parameterized by a distortion parameter and dependent + * on the squared distance to the center of the image. The distorted pixel is then computed as the + * scalar multiplication of the pixel coordinates with the value returned by this model. See the + * compute_distorted_uv function for more details. */ +float compute_distortion_scale(float distortion, float distance_squared) +{ + return 1.0 / (1.0 + sqrt(max(0.0, 1.0 - distortion * distance_squared))); +} + +/* A vectorized version of compute_distortion_scale that is applied on the chromatic distortion + * parameters passed to the shader. */ +vec3 compute_chromatic_distortion_scale(float distance_squared) +{ + return 1.0 / (1.0 + sqrt(max(vec3(0.0), 1.0 - chromatic_distortion * distance_squared))); +} + +/* Compute the image coordinates after distortion by the given distortion scale computed by the + * compute_distortion_scale function. Note that the function expects centered normalized UV + * coordinates but outputs non-centered image coordinates. */ +vec2 compute_distorted_uv(vec2 uv, float scale) +{ + return (uv * scale + 0.5) * texture_size(input_tx) - 0.5; +} + +/* Compute the number of integration steps that should be used to approximate the distorted pixel + * using a heuristic, see the compute_number_of_steps function for more details. The numbers of + * steps is proportional to the number of pixels spanned by the distortion amount. For jitter + * distortion, the square root of the distortion amount plus 1 is used with a minimum of 2 steps. + * For non-jitter distortion, the distortion amount plus 1 is used as the number of steps */ +int compute_number_of_integration_steps_heuristic(float distortion) +{ +#if defined(JITTER) + return distortion < 4.0 ? 2 : int(sqrt(distortion + 1.0)); +#else + return int(distortion + 1.0); +#endif +} + +/* Compute the number of integration steps that should be used to compute each channel of the + * distorted pixel. Each of the channels are distorted by their respective chromatic distortion + * amount, then the amount of distortion between each two consecutive channels is computed, this + * amount is then used to heuristically infer the number of needed integration steps, see the + * integrate_distortion function for more information. */ +ivec3 compute_number_of_integration_steps(vec2 uv, float distance_squared) +{ + /* Distort each channel by its respective chromatic distortion amount. */ + vec3 distortion_scale = compute_chromatic_distortion_scale(distance_squared); + vec2 distorted_uv_red = compute_distorted_uv(uv, distortion_scale.r); + vec2 distorted_uv_green = compute_distorted_uv(uv, distortion_scale.g); + vec2 distorted_uv_blue = compute_distorted_uv(uv, distortion_scale.b); + + /* Infer the number of needed integration steps to compute the distorted red channel starting + * from the green channel. */ + float distortion_red = distance(distorted_uv_red, distorted_uv_green); + int steps_red = compute_number_of_integration_steps_heuristic(distortion_red); + + /* Infer the number of needed integration steps to compute the distorted blue channel starting + * from the green channel. */ + float distortion_blue = distance(distorted_uv_green, distorted_uv_blue); + int steps_blue = compute_number_of_integration_steps_heuristic(distortion_blue); + + /* The number of integration steps used to compute the green channel is the sum of both the red + * and the blue channel steps because it is computed once with each of them. */ + return ivec3(steps_red, steps_red + steps_blue, steps_blue); +} + +/* Returns a random jitter amount, which is essentially a random value in the [0, 1] range. If + * jitter is not enabled, return a constant 0.5 value instead. */ +float get_jitter(int seed) +{ +#if defined(JITTER) + return hash_uint3_to_float(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y, seed); +#else + return 0.5; +#endif +} + +/* Each color channel may have a different distortion with the guarantee that the red will have the + * lowest distortion while the blue will have the highest one. If each channel is distorted + * independently, the image will look disintegrated, with each channel seemingly merely shifted. + * Consequently, the distorted pixels needs to be computed by integrating along the path of change + * of distortion starting from one channel to another. For instance, to compute the distorted red + * from the distorted green, we accumulate the color of the distorted pixel starting from the + * distortion of the red, taking small steps until we reach the distortion of the green. The pixel + * color is weighted such that it is maximum at the start distortion and zero at the end distortion + * in an arithmetic progression. The integration steps can be augmented with random values to + * simulate lens jitter. Finally, it should be noted that this function integrates both the start + * and end channels in reverse directions for more efficient computation. */ +vec3 integrate_distortion(int start, int end, float distance_squared, vec2 uv, int steps) +{ + vec3 accumulated_color = vec3(0.0); + float distortion_amount = chromatic_distortion[end] - chromatic_distortion[start]; + for (int i = 0; i < steps; i++) { + /* The increment will be in the [0, 1) range across iterations. */ + float increment = (i + get_jitter(i)) / steps; + float distortion = chromatic_distortion[start] + increment * distortion_amount; + float distortion_scale = compute_distortion_scale(distortion, distance_squared); + + /* Sample the color at the distorted coordinates and accumulate it weighted by the increment + * value for both the start and end channels. */ + vec2 distorted_uv = compute_distorted_uv(uv, distortion_scale); + vec4 color = texture(input_tx, distorted_uv / texture_size(input_tx)); + accumulated_color[start] += (1.0 - increment) * color[start]; + accumulated_color[end] += increment * color[end]; + } + return accumulated_color; +} + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + + /* Compute the UV image coordinates in the range [-1, 1] as well as the squared distance to the + * center of the image, which is at (0, 0) in the UV coordinates. */ + vec2 center = texture_size(input_tx) / 2.0; + vec2 uv = scale * (texel + 0.5 - center) / center; + float distance_squared = dot(uv, uv); + + /* If any of the color channels will get distorted outside of the screen beyond what is possible, + * write a zero transparent color and return. */ + if (any(greaterThan(chromatic_distortion * distance_squared, vec3(1.0)))) { + imageStore(output_img, texel, vec4(0.0)); + return; + } + + /* Compute the number of integration steps that should be used to compute each channel of the + * distorted pixel. */ + ivec3 number_of_steps = compute_number_of_integration_steps(uv, distance_squared); + + /* Integrate the distortion of the red and green, then the green and blue channels. That means + * the green will be integrated twice, but this is accounted for in the number of steps which the + * color will later be divided by. See the compute_number_of_integration_steps function for more + * details. */ + vec3 color = vec3(0.0); + color += integrate_distortion(0, 1, distance_squared, uv, number_of_steps.r); + color += integrate_distortion(1, 2, distance_squared, uv, number_of_steps.b); + + /* The integration above performed weighted accumulation, and thus the color needs to be divided + * by the sum of the weights. Assuming no jitter, the weights are generated as an arithmetic + * progression starting from (0.5 / n) to ((n - 0.5) / n) for n terms. The sum of an arithmetic + * progression can be computed as (n * (start + end) / 2), which when subsisting the start and + * end reduces to (n / 2). So the color should be multiplied by 2 / n. The jitter sequence + * approximately sums to the same value because it is a uniform random value whose mean value is + * 0.5, so the expression doesn't change regardless of jitter. */ + color *= 2.0 / vec3(number_of_steps); + + imageStore(output_img, texel, vec4(color, 1.0)); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl new file mode 100644 index 00000000000..7dd40581790 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_set_alpha.glsl @@ -0,0 +1,8 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); + vec4 color = vec4(texture_load(image_tx, texel).rgb, texture_load(alpha_tx, texel).x); + imageStore(output_img, texel, color); +} diff --git a/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl new file mode 100644 index 00000000000..866b9045da2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/compositor_split_viewer.glsl @@ -0,0 +1,14 @@ +#pragma BLENDER_REQUIRE(gpu_shader_compositor_texture_utilities.glsl) + +void main() +{ + ivec2 texel = ivec2(gl_GlobalInvocationID.xy); +#if defined(SPLIT_HORIZONTAL) + bool condition = (view_size.x * split_ratio) < texel.x; +#elif defined(SPLIT_VERTICAL) + bool condition = (view_size.y * split_ratio) < texel.y; +#endif + vec4 color = condition ? texture_load(first_image_tx, texel) : + texture_load(second_image_tx, texel); + imageStore(output_img, texel, color); +} diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh new file mode 100644 index 00000000000..11f2f329cd8 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_alpha_crop_info.hh @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_alpha_crop) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "lower_bound") + .push_constant(Type::IVEC2, "upper_bound") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_alpha_crop.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh new file mode 100644 index 00000000000..ecb253bbab1 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_box_mask_info.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_box_mask_shared) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "domain_size") + .push_constant(Type::VEC2, "location") + .push_constant(Type::VEC2, "size") + .push_constant(Type::FLOAT, "cos_angle") + .push_constant(Type::FLOAT, "sin_angle") + .sampler(0, ImageType::FLOAT_2D, "base_mask_tx") + .sampler(1, ImageType::FLOAT_2D, "mask_value_tx") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img") + .compute_source("compositor_box_mask.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_box_mask_add) + .additional_info("compositor_box_mask_shared") + .define("CMP_NODE_MASKTYPE_ADD") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_box_mask_subtract) + .additional_info("compositor_box_mask_shared") + .define("CMP_NODE_MASKTYPE_SUBTRACT") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_box_mask_multiply) + .additional_info("compositor_box_mask_shared") + .define("CMP_NODE_MASKTYPE_MULTIPLY") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_box_mask_not) + .additional_info("compositor_box_mask_shared") + .define("CMP_NODE_MASKTYPE_NOT") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh new file mode 100644 index 00000000000..35e60056736 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_convert_info.hh @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_convert_shared) + .local_group_size(16, 16) + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .typedef_source("gpu_shader_compositor_type_conversion.glsl") + .compute_source("compositor_convert.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_convert_float_to_vector) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_float(value.x), 0.0)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_float_to_color) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4_from_float(value.x)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_float) + .additional_info("compositor_convert_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(float_from_vec4(value), vec3(0.0))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_vector) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(vec3_from_vec4(value), 0.0)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_vector_to_float) + .additional_info("compositor_convert_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(float_from_vec3(value.xyz), vec3(0.0))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_vector_to_color) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4_from_vec3(value.xyz)") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_extract_alpha_from_color) + .additional_info("compositor_convert_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(value.a, vec3(0.0))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_half_color) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "value") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_float_to_half_float) + .additional_info("compositor_convert_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(value.r, vec3(0.0))") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_convert_color_to_opaque) + .additional_info("compositor_convert_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .define("CONVERT_EXPRESSION(value)", "vec4(value.rgb, 1.0)") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh new file mode 100644 index 00000000000..52db91c94e5 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_ellipse_mask_info.hh @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_shared) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "domain_size") + .push_constant(Type::VEC2, "location") + .push_constant(Type::VEC2, "radius") + .push_constant(Type::FLOAT, "cos_angle") + .push_constant(Type::FLOAT, "sin_angle") + .sampler(0, ImageType::FLOAT_2D, "base_mask_tx") + .sampler(1, ImageType::FLOAT_2D, "mask_value_tx") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_mask_img") + .compute_source("compositor_ellipse_mask.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_add) + .additional_info("compositor_ellipse_mask_shared") + .define("CMP_NODE_MASKTYPE_ADD") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_subtract) + .additional_info("compositor_ellipse_mask_shared") + .define("CMP_NODE_MASKTYPE_SUBTRACT") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_multiply) + .additional_info("compositor_ellipse_mask_shared") + .define("CMP_NODE_MASKTYPE_MULTIPLY") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_ellipse_mask_not) + .additional_info("compositor_ellipse_mask_shared") + .define("CMP_NODE_MASKTYPE_NOT") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh new file mode 100644 index 00000000000..db831518cb7 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_flip_info.hh @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_flip) + .local_group_size(16, 16) + .push_constant(Type::BOOL, "flip_x") + .push_constant(Type::BOOL, "flip_y") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_flip.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh new file mode 100644 index 00000000000..e7736744c40 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_image_crop_info.hh @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_image_crop) + .local_group_size(16, 16) + .push_constant(Type::IVEC2, "lower_bound") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_image_crop.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh new file mode 100644 index 00000000000..98fe1731703 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_projector_lens_distortion_info.hh @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_projector_lens_distortion) + .local_group_size(16, 16) + .push_constant(Type::FLOAT, "dispersion") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_projector_lens_distortion.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh new file mode 100644 index 00000000000..4528649ae98 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_realize_on_domain_info.hh @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_shared) + .local_group_size(16, 16) + .push_constant(Type::MAT4, "inverse_transformation") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .compute_source("compositor_realize_on_domain.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_color) + .additional_info("compositor_realize_on_domain_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_vector) + .additional_info("compositor_realize_on_domain_shared") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_realize_on_domain_float) + .additional_info("compositor_realize_on_domain_shared") + .image(0, GPU_R16F, Qualifier::WRITE, ImageType::FLOAT_2D, "domain_img") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh new file mode 100644 index 00000000000..c42f2b328d4 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_screen_lens_distortion_info.hh @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion_shared) + .local_group_size(16, 16) + .push_constant(Type::VEC3, "chromatic_distortion") + .push_constant(Type::FLOAT, "scale") + .sampler(0, ImageType::FLOAT_2D, "input_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_screen_lens_distortion.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion) + .additional_info("compositor_screen_lens_distortion_shared") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_screen_lens_distortion_jitter) + .additional_info("compositor_screen_lens_distortion_shared") + .define("JITTER") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh new file mode 100644 index 00000000000..ca28194e921 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_set_alpha_info.hh @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_set_alpha) + .local_group_size(16, 16) + .sampler(0, ImageType::FLOAT_2D, "image_tx") + .sampler(1, ImageType::FLOAT_2D, "alpha_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_set_alpha.glsl") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh new file mode 100644 index 00000000000..d5793b0ce59 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/infos/compositor_split_viewer_info.hh @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "gpu_shader_create_info.hh" + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_shared) + .local_group_size(16, 16) + .push_constant(Type::FLOAT, "split_ratio") + .push_constant(Type::IVEC2, "view_size") + .sampler(0, ImageType::FLOAT_2D, "first_image_tx") + .sampler(1, ImageType::FLOAT_2D, "second_image_tx") + .image(0, GPU_RGBA16F, Qualifier::WRITE, ImageType::FLOAT_2D, "output_img") + .compute_source("compositor_split_viewer.glsl"); + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_horizontal) + .additional_info("compositor_split_viewer_shared") + .define("SPLIT_HORIZONTAL") + .do_static_compilation(true); + +GPU_SHADER_CREATE_INFO(compositor_split_viewer_vertical) + .additional_info("compositor_split_viewer_shared") + .define("SPLIT_VERTICAL") + .do_static_compilation(true); diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl new file mode 100644 index 00000000000..8e3e033147f --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_alpha_over.glsl @@ -0,0 +1,48 @@ +void node_composite_alpha_over_mixed( + float factor, vec4 color, vec4 over_color, float premultiply_factor, out vec4 result) +{ + if (over_color.a <= 0.0) { + result = color; + } + else if (factor == 1.0 && over_color.a >= 1.0) { + result = over_color; + } + else { + float add_factor = 1.0 - premultiply_factor + over_color.a * premultiply_factor; + float premultiplier = factor * add_factor; + float multiplier = 1.0 - factor * over_color.a; + + result = multiplier * color + vec2(premultiplier, factor).xxxy * over_color; + } +} + +void node_composite_alpha_over_key(float factor, vec4 color, vec4 over_color, out vec4 result) +{ + if (over_color.a <= 0.0) { + result = color; + } + else if (factor == 1.0 && over_color.a >= 1.0) { + result = over_color; + } + else { + result = mix(color, vec4(over_color.rgb, 1.0), factor * over_color.a); + } +} + +void node_composite_alpha_over_premultiply(float factor, + vec4 color, + vec4 over_color, + out vec4 result) +{ + if (over_color.a < 0.0) { + result = color; + } + else if (factor == 1.0 && over_color.a >= 1.0) { + result = over_color; + } + else { + float multiplier = 1.0 - factor * over_color.a; + + result = multiplier * color + factor * over_color; + } +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl new file mode 100644 index 00000000000..ce71b4fd8a4 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_bright_contrast.glsl @@ -0,0 +1,38 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +/* The algorithm is by Werner D. Streidt + * (http://visca.com/ffactory/archives/5-99/msg00021.html) + * Extracted of OpenCV demhist.c + */ + +#define FLT_EPSILON 1.192092896e-07F + +void node_composite_bright_contrast( + vec4 color, float brightness, float contrast, const float use_premultiply, out vec4 result) +{ + brightness /= 100.0; + float delta = contrast / 200.0; + + float multiplier, offset; + if (contrast > 0.0) { + multiplier = 1.0 - delta * 2.0; + multiplier = 1.0 / max(multiplier, FLT_EPSILON); + offset = multiplier * (brightness - delta); + } + else { + delta *= -1.0; + multiplier = max(1.0 - delta * 2.0, 0.0); + offset = multiplier * brightness + delta; + } + + if (use_premultiply != 0.0) { + color_alpha_unpremultiply(color, color); + } + + result.rgb = color.rgb * multiplier + offset; + result.a = color.a; + + if (use_premultiply != 0.0) { + color_alpha_premultiply(result, result); + } +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl new file mode 100644 index 00000000000..f2dcc9543f2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_channel_matte.glsl @@ -0,0 +1,52 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +#define CMP_NODE_CHANNEL_MATTE_CS_RGB 1.0 +#define CMP_NODE_CHANNEL_MATTE_CS_HSV 2.0 +#define CMP_NODE_CHANNEL_MATTE_CS_YUV 3.0 +#define CMP_NODE_CHANNEL_MATTE_CS_YCC 4.0 + +void node_composite_channel_matte(vec4 color, + const float color_space, + const float matte_channel, + const vec2 limit_channels, + float max_limit, + float min_limit, + out vec4 result, + out float matte) +{ + vec4 channels; + if (color_space == CMP_NODE_CHANNEL_MATTE_CS_HSV) { + rgb_to_hsv(color, channels); + } + else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YUV) { + rgba_to_yuva_itu_709(color, channels); + } + else if (color_space == CMP_NODE_CHANNEL_MATTE_CS_YCC) { + rgba_to_ycca_itu_709(color, channels); + } + else { + channels = color; + } + + float matte_value = channels[int(matte_channel)]; + float limit_value = max(channels[int(limit_channels.x)], channels[int(limit_channels.y)]); + + float alpha = 1.0 - (matte_value - limit_value); + if (alpha > max_limit) { + alpha = color.a; + } + else if (alpha < min_limit) { + alpha = 0.0; + } + else { + alpha = (alpha - min_limit) / (max_limit - min_limit); + } + + matte = min(alpha, color.a); + result = color * matte; +} + +#undef CMP_NODE_CHANNEL_MATTE_CS_RGB +#undef CMP_NODE_CHANNEL_MATTE_CS_HSV +#undef CMP_NODE_CHANNEL_MATTE_CS_YUV +#undef CMP_NODE_CHANNEL_MATTE_CS_YCC diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl new file mode 100644 index 00000000000..5d6bea0c9db --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_chroma_matte.glsl @@ -0,0 +1,43 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +/* Algorithm from the book Video Demystified. Chapter 7. Chroma Keying. */ +void node_composite_chroma_matte(vec4 color, + vec4 key, + float acceptance, + float cutoff, + float falloff, + out vec4 result, + out float matte) +{ + vec4 color_ycca; + rgba_to_ycca_itu_709(color, color_ycca); + vec4 key_ycca; + rgba_to_ycca_itu_709(key, key_ycca); + + /* Normalize the CrCb components into the [-1, 1] range. */ + vec2 color_cc = color_ycca.yz * 2.0 - 1.0; + vec2 key_cc = key_ycca.yz * 2.0 - 1.0; + + /* Rotate the color onto the space of the key such that x axis of the color space passes through + * the key color. */ + color_cc = vector_to_rotation_matrix(key_cc * vec2(1.0, -1.0)) * color_cc; + + /* Compute foreground key. If positive, the value is in the [0, 1] range. */ + float foreground_key = color_cc.x - (abs(color_cc.y) / acceptance); + + /* Negative foreground key values retain the original alpha. Positive values are scaled by the + * falloff, while colors that make an angle less than the cutoff angle get a zero alpha. */ + float alpha = color.a; + if (foreground_key > 0.0) { + alpha = 1.0 - (foreground_key / falloff); + + if (abs(atan(color_cc.y, color_cc.x)) < (cutoff / 2.0)) { + alpha = 0.0; + } + } + + /* Compute output. */ + matte = min(alpha, color.a); + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl new file mode 100644 index 00000000000..bffb94cdedb --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_balance.glsl @@ -0,0 +1,34 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_color_balance_lgg( + float factor, vec4 color, vec3 lift, vec3 gamma, vec3 gain, out vec4 result) +{ + lift = 2.0 - lift; + vec3 srgb_color = linear_rgb_to_srgb(color.rgb); + vec3 lift_balanced = ((srgb_color - 1.0) * lift) + 1.0; + + vec3 gain_balanced = lift_balanced * gain; + gain_balanced = max(gain_balanced, vec3(0.0)); + + vec3 linear_color = srgb_to_linear_rgb(gain_balanced); + gamma = mix(gamma, vec3(1e-6), equal(gamma, vec3(0.0))); + vec3 gamma_balanced = pow(linear_color, 1.0 / gamma); + + result.rgb = mix(color.rgb, gamma_balanced, min(factor, 1.0)); + result.a = color.a; +} + +void node_composite_color_balance_asc_cdl(float factor, + vec4 color, + vec3 offset, + vec3 power, + vec3 slope, + float offset_basis, + out vec4 result) +{ + offset += offset_basis; + vec3 balanced = color.rgb * slope + offset; + balanced = pow(max(balanced, vec3(0.0)), power); + result.rgb = mix(color.rgb, balanced, min(factor, 1.0)); + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl new file mode 100644 index 00000000000..9b4858f03be --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_correction.glsl @@ -0,0 +1,87 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_color_correction(vec4 color, + float mask, + const vec3 enabled_channels, + float start_midtones, + float end_midtones, + float master_saturation, + float master_contrast, + float master_gamma, + float master_gain, + float master_lift, + float shadows_saturation, + float shadows_contrast, + float shadows_gamma, + float shadows_gain, + float shadows_lift, + float midtones_saturation, + float midtones_contrast, + float midtones_gamma, + float midtones_gain, + float midtones_lift, + float highlights_saturation, + float highlights_contrast, + float highlights_gamma, + float highlights_gain, + float highlights_lift, + const vec3 luminance_coefficients, + out vec4 result) +{ + const float margin = 0.10; + const float margin_divider = 0.5 / margin; + float level = (color.r + color.g + color.b) / 3.0; + float level_shadows = 0.0; + float level_midtones = 0.0; + float level_highlights = 0.0; + if (level < (start_midtones - margin)) { + level_shadows = 1.0; + } + else if (level < (start_midtones + margin)) { + level_midtones = ((level - start_midtones) * margin_divider) + 0.5; + level_shadows = 1.0 - level_midtones; + } + else if (level < (end_midtones - margin)) { + level_midtones = 1.0; + } + else if (level < (end_midtones + margin)) { + level_highlights = ((level - end_midtones) * margin_divider) + 0.5; + level_midtones = 1.0 - level_highlights; + } + else { + level_highlights = 1.0; + } + + float contrast = level_shadows * shadows_contrast; + contrast += level_midtones * midtones_contrast; + contrast += level_highlights * highlights_contrast; + contrast *= master_contrast; + float saturation = level_shadows * shadows_saturation; + saturation += level_midtones * midtones_saturation; + saturation += level_highlights * highlights_saturation; + saturation *= master_saturation; + float gamma = level_shadows * shadows_gamma; + gamma += level_midtones * midtones_gamma; + gamma += level_highlights * highlights_gamma; + gamma *= master_gamma; + float gain = level_shadows * shadows_gain; + gain += level_midtones * midtones_gain; + gain += level_highlights * highlights_gain; + gain *= master_gain; + float lift = level_shadows * shadows_lift; + lift += level_midtones * midtones_lift; + lift += level_highlights * highlights_lift; + lift += master_lift; + + float inverse_gamma = 1.0 / gamma; + float luma = get_luminance(color.rgb, luminance_coefficients); + + vec3 corrected = luma + saturation * (color.rgb - luma); + corrected = 0.5 + (corrected - 0.5) * contrast; + corrected = fallback_pow(corrected * gain + lift, inverse_gamma, corrected); + corrected = mix(color.rgb, corrected, min(mask, 1.0)); + + result.rgb = mix(corrected, color.rgb, equal(enabled_channels, vec3(0.0))); + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl new file mode 100644 index 00000000000..038471bc1bc --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_matte.glsl @@ -0,0 +1,27 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_color_matte(vec4 color, + vec4 key, + float hue_epsilon, + float saturation_epsilon, + float value_epsilon, + out vec4 result, + out float matte) + +{ + vec4 color_hsva; + rgb_to_hsv(color, color_hsva); + vec4 key_hsva; + rgb_to_hsv(key, key_hsva); + + bool is_within_saturation = distance(color_hsva.y, key_hsva.y) < saturation_epsilon; + bool is_within_value = distance(color_hsva.z, key_hsva.z) < value_epsilon; + bool is_within_hue = distance(color_hsva.x, key_hsva.x) < hue_epsilon; + /* Hue wraps around, so check the distance around the boundary. */ + float min_hue = min(color_hsva.x, key_hsva.x); + float max_hue = max(color_hsva.x, key_hsva.x); + is_within_hue = is_within_hue || ((min_hue + (1.0 - max_hue)) < hue_epsilon); + + matte = (is_within_hue && is_within_saturation && is_within_value) ? 0.0 : color.a; + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl new file mode 100644 index 00000000000..0adad53ad80 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_spill.glsl @@ -0,0 +1,13 @@ +void node_composite_color_spill(vec4 color, + float factor, + const float spill_channel, + vec3 spill_scale, + const vec2 limit_channels, + float limit_scale, + out vec4 result) +{ + float average_limit = (color[int(limit_channels.x)] + color[int(limit_channels.y)]) / 2.0; + float map = factor * color[int(spill_channel)] - limit_scale * average_limit; + result.rgb = map > 0.0 ? color.rgb + spill_scale * map : color.rgb; + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl new file mode 100644 index 00000000000..bcdd625bd4f --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_color_to_luminance.glsl @@ -0,0 +1,6 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void color_to_luminance(vec4 color, const vec3 luminance_coefficients, out float result) +{ + result = get_luminance(color.rgb, luminance_coefficients); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl new file mode 100644 index 00000000000..d769cadce3c --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_difference_matte.glsl @@ -0,0 +1,10 @@ +void node_composite_difference_matte( + vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte) +{ + vec4 difference = abs(color - key); + float average_difference = (difference.r + difference.g + difference.b) / 3.0; + bool is_opaque = average_difference > tolerance + falloff; + float alpha = is_opaque ? color.a : (max(0.0, average_difference - tolerance) / falloff); + matte = min(alpha, color.a); + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl new file mode 100644 index 00000000000..9beed66826c --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_distance_matte.glsl @@ -0,0 +1,26 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_distance_matte_rgba( + vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte) +{ + float difference = distance(color.rgb, key.rgb); + bool is_opaque = difference > tolerance + falloff; + float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff; + matte = min(alpha, color.a); + result = color * matte; +} + +void node_composite_distance_matte_ycca( + vec4 color, vec4 key, float tolerance, float falloff, out vec4 result, out float matte) +{ + vec4 color_ycca; + rgba_to_ycca_itu_709(color, color_ycca); + vec4 key_ycca; + rgba_to_ycca_itu_709(key, key_ycca); + + float difference = distance(color_ycca.yz, key_ycca.yz); + bool is_opaque = difference > tolerance + falloff; + float alpha = is_opaque ? color.a : max(0.0, difference - tolerance) / falloff; + matte = min(alpha, color.a); + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl new file mode 100644 index 00000000000..f246635a91e --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_exposure.glsl @@ -0,0 +1,6 @@ +void node_composite_exposure(vec4 color, float exposure, out vec4 result) +{ + float multiplier = exp2(exposure); + result.rgb = color.rgb * multiplier; + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl new file mode 100644 index 00000000000..53070d4b0e2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_gamma.glsl @@ -0,0 +1,7 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_math_utils.glsl) + +void node_composite_gamma(vec4 color, float gamma, out vec4 result) +{ + result.rgb = fallback_pow(color.rgb, gamma, color.rgb); + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl new file mode 100644 index 00000000000..99eb125cdf2 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_correct.glsl @@ -0,0 +1,39 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +/* 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 node_composite_hue_correct(float factor, + vec4 color, + sampler1DArray curve_map, + const float layer, + vec3 minimums, + vec3 range_dividers, + out vec4 result) +{ + vec4 hsv; + rgb_to_hsv(color, hsv); + + /* First, adjust the hue channel on its own, since corrections in the saturation and value + * channels depends on the new value of the hue, not its original value. A curve map value of 0.5 + * means no change in hue, so adjust the value to get an identity at 0.5. Since the identity of + * addition is 0, we subtract 0.5 (0.5 - 0.5 = 0). */ + const float hue_parameter = NORMALIZE_PARAMETER(hsv.x, minimums.x, range_dividers.x); + hsv.x += texture(curve_map, vec2(hue_parameter, layer)).x - 0.5; + + /* Second, adjust the saturation and value based on the new value of the hue. A curve map value + * of 0.5 means no change in hue, so adjust the value to get an identity at 0.5. Since the + * identity of duplication is 1, we multiply by 2 (0.5 * 2 = 1). */ + vec2 parameters = NORMALIZE_PARAMETER(hsv.x, minimums.yz, range_dividers.yz); + hsv.y *= texture(curve_map, vec2(parameters.x, layer)).y * 2.0; + hsv.z *= texture(curve_map, vec2(parameters.y, layer)).z * 2.0; + + /* Sanitize the new hue and saturation values. */ + hsv.x = fract(hsv.x); + hsv.y = clamp(hsv.y, 0.0, 1.0); + + hsv_to_rgb(hsv, result); + + result = mix(color, result, factor); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl new file mode 100644 index 00000000000..dd5eb33d318 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_hue_saturation_value.glsl @@ -0,0 +1,16 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_hue_saturation_value( + vec4 color, float hue, float saturation, float value, float factor, out vec4 result) +{ + vec4 hsv; + rgb_to_hsv(color, hsv); + + hsv.x = fract(hsv.x + hue + 0.5); + hsv.y = clamp(hsv.y * saturation, 0.0, 1.0); + hsv.z = hsv.z * value; + + hsv_to_rgb(hsv, result); + + result = mix(color, result, factor); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl new file mode 100644 index 00000000000..59be746da7f --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_invert.glsl @@ -0,0 +1,13 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_invert(float fac, vec4 color, float do_rgb, float do_alpha, out vec4 result) +{ + result = color; + if (do_rgb != 0.0) { + result.rgb = 1.0 - result.rgb; + } + if (do_alpha != 0.0) { + result.a = 1.0 - result.a; + } + result = mix(color, result, fac); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl new file mode 100644 index 00000000000..3647ac583fe --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_luminance_matte.glsl @@ -0,0 +1,14 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +void node_composite_luminance_matte(vec4 color, + float high, + float low, + const vec3 luminance_coefficients, + out vec4 result, + out float matte) +{ + float luminance = get_luminance(color.rgb, luminance_coefficients); + float alpha = clamp(0.0, 1.0, (luminance - low) / (high - low)); + matte = min(alpha, color.a); + result = color * matte; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl new file mode 100644 index 00000000000..27624223dbc --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_main.glsl @@ -0,0 +1,7 @@ +/* The compute shader that will be dispatched by the compositor ShaderOperation. It just calls the + * evaluate function that will be dynamically generated and appended to this shader in the + * ShaderOperation::generate_code method. */ +void main() +{ + evaluate(); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl new file mode 100644 index 00000000000..20874b4ef44 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_map_value.glsl @@ -0,0 +1,56 @@ +/* An arbitrary value determined by Blender. */ +#define BLENDER_ZMAX 10000.0 + +void node_composite_map_range(float value, + float from_min, + float from_max, + float to_min, + float to_max, + const float should_clamp, + out float result) +{ + if (abs(from_max - from_min) < 1e-6) { + result = 0.0; + } + else { + if (value >= -BLENDER_ZMAX && value <= BLENDER_ZMAX) { + result = (value - from_min) / (from_max - from_min); + result = to_min + result * (to_max - to_min); + } + else if (value > BLENDER_ZMAX) { + result = to_max; + } + else { + result = to_min; + } + + if (should_clamp != 0.0) { + if (to_max > to_min) { + result = clamp(result, to_min, to_max); + } + else { + result = clamp(result, to_max, to_min); + } + } + } +} + +void node_composite_map_value(float value, + float offset, + float size, + const float use_min, + float min, + const float use_max, + float max, + out float result) +{ + result = (value + offset) * size; + + if (use_min != 0.0 && result < min) { + result = min; + } + + if (use_max != 0.0 && result > max) { + result = max; + } +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl new file mode 100644 index 00000000000..a2e3b6c4aaa --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_normal.glsl @@ -0,0 +1,9 @@ +void node_composite_normal(vec3 input_vector, + vec3 input_normal, + out vec3 result_normal, + out float result_dot) +{ + vec3 normal = normalize(input_normal); + result_normal = normal; + result_dot = -dot(input_vector, normal); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl new file mode 100644 index 00000000000..ee8ae234abe --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_posterize.glsl @@ -0,0 +1,6 @@ +void node_composite_posterize(vec4 color, float steps, out vec4 result) +{ + steps = clamp(steps, 2.0, 1024.0); + result = floor(color * steps) / steps; + result.a = color.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl new file mode 100644 index 00000000000..d72d2260394 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_separate_combine.glsl @@ -0,0 +1,132 @@ +#pragma BLENDER_REQUIRE(gpu_shader_common_color_utils.glsl) + +/* ** Combine/Separate XYZ ** */ + +void node_composite_combine_xyz(float x, float y, float z, out vec3 vector) +{ + vector = vec3(x, y, z); +} + +void node_composite_separate_xyz(vec3 vector, out float x, out float y, out float z) +{ + x = vector.x; + y = vector.y; + z = vector.z; +} + +/* ** Combine/Separate RGBA ** */ + +void node_composite_combine_rgba(float r, float g, float b, float a, out vec4 color) +{ + color = vec4(r, g, b, a); +} + +void node_composite_separate_rgba(vec4 color, out float r, out float g, out float b, out float a) +{ + r = color.r; + g = color.g; + b = color.b; + a = color.a; +} + +/* ** Combine/Separate HSVA ** */ + +void node_composite_combine_hsva(float h, float s, float v, float a, out vec4 color) +{ + hsv_to_rgb(vec4(h, s, v, a), color); +} + +void node_composite_separate_hsva(vec4 color, out float h, out float s, out float v, out float a) +{ + vec4 hsva; + rgb_to_hsv(color, hsva); + h = hsva.x; + s = hsva.y; + v = hsva.z; + a = hsva.a; +} + +/* ** Combine/Separate HSLA ** */ + +void node_composite_combine_hsla(float h, float s, float l, float a, out vec4 color) +{ + hsl_to_rgb(vec4(h, s, l, a), color); +} + +void node_composite_separate_hsla(vec4 color, out float h, out float s, out float l, out float a) +{ + vec4 hsla; + rgb_to_hsl(color, hsla); + h = hsla.x; + s = hsla.y; + l = hsla.z; + a = hsla.a; +} + +/* ** Combine/Separate YCCA ** */ + +void node_composite_combine_ycca_itu_601(float y, float cb, float cr, float a, out vec4 color) +{ + ycca_to_rgba_itu_601(vec4(y, cb, cr, a), color); +} + +void node_composite_combine_ycca_itu_709(float y, float cb, float cr, float a, out vec4 color) +{ + ycca_to_rgba_itu_709(vec4(y, cb, cr, a), color); +} + +void node_composite_combine_ycca_jpeg(float y, float cb, float cr, float a, out vec4 color) +{ + ycca_to_rgba_jpeg(vec4(y, cb, cr, a), color); +} + +void node_composite_separate_ycca_itu_601( + vec4 color, out float y, out float cb, out float cr, out float a) +{ + vec4 ycca; + rgba_to_ycca_itu_601(color, ycca); + y = ycca.x; + cb = ycca.y; + cr = ycca.z; + a = ycca.a; +} + +void node_composite_separate_ycca_itu_709( + vec4 color, out float y, out float cb, out float cr, out float a) +{ + vec4 ycca; + rgba_to_ycca_itu_709(color, ycca); + y = ycca.x; + cb = ycca.y; + cr = ycca.z; + a = ycca.a; +} + +void node_composite_separate_ycca_jpeg( + vec4 color, out float y, out float cb, out float cr, out float a) +{ + vec4 ycca; + rgba_to_ycca_jpeg(color, ycca); + y = ycca.x; + cb = ycca.y; + cr = ycca.z; + a = ycca.a; +} + +/* ** Combine/Separate YUVA ** */ + +void node_composite_combine_yuva_itu_709(float y, float u, float v, float a, out vec4 color) +{ + yuva_to_rgba_itu_709(vec4(y, u, v, a), color); +} + +void node_composite_separate_yuva_itu_709( + vec4 color, out float y, out float u, out float v, out float a) +{ + vec4 yuva; + rgba_to_yuva_itu_709(color, yuva); + y = yuva.x; + u = yuva.y; + v = yuva.z; + a = yuva.a; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl new file mode 100644 index 00000000000..95380d1ed0f --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_set_alpha.glsl @@ -0,0 +1,9 @@ +void node_composite_set_alpha_apply(vec4 color, float alpha, out vec4 result) +{ + result = color * alpha; +} + +void node_composite_set_alpha_replace(vec4 color, float alpha, out vec4 result) +{ + result = vec4(color.rgb, alpha); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl new file mode 100644 index 00000000000..7fba26907b5 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_store_output.glsl @@ -0,0 +1,26 @@ +/* The following functions are called to store the given value in the output identified by the + * given ID. The ID is an unsigned integer that is encoded in a float, so floatBitsToUint is called + * to get the actual identifier. The functions have an output value as their last argument that is + * used to establish an output link that is then used to track the nodes that contribute to the + * output of the compositor node tree. + * + * The store_[float|vector|color] functions are dynamically generated in + * ShaderOperation::generate_code_for_outputs. */ + +void node_compositor_store_output_float(const float id, float value, out float out_value) +{ + store_float(floatBitsToUint(id), value); + out_value = value; +} + +void node_compositor_store_output_vector(const float id, vec3 vector, out vec3 out_vector) +{ + store_vector(floatBitsToUint(id), vector); + out_vector = vector; +} + +void node_compositor_store_output_color(const float id, vec4 color, out vec4 out_color) +{ + store_color(floatBitsToUint(id), color); + out_color = color; +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl new file mode 100644 index 00000000000..00e9a391097 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_texture_utilities.glsl @@ -0,0 +1,25 @@ +/* A shorthand for 1D textureSize with a zero LOD. */ +int texture_size(sampler1D sampler) +{ + return textureSize(sampler, 0); +} + +/* A shorthand for 1D texelFetch with zero LOD and bounded access clamped to border. */ +vec4 texture_load(sampler1D sampler, int x) +{ + const int texture_bound = texture_size(sampler) - 1; + return texelFetch(sampler, clamp(x, 0, texture_bound), 0); +} + +/* A shorthand for 2D textureSize with a zero LOD. */ +ivec2 texture_size(sampler2D sampler) +{ + return textureSize(sampler, 0); +} + +/* A shorthand for 2D texelFetch with zero LOD and bounded access clamped to border. */ +vec4 texture_load(sampler2D sampler, ivec2 texel) +{ + const ivec2 texture_bounds = texture_size(sampler) - ivec2(1); + return texelFetch(sampler, clamp(texel, ivec2(0), texture_bounds), 0); +} diff --git a/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl new file mode 100644 index 00000000000..75c76fd7341 --- /dev/null +++ b/source/blender/gpu/shaders/compositor/library/gpu_shader_compositor_type_conversion.glsl @@ -0,0 +1,29 @@ +float float_from_vec4(vec4 vector) +{ + return dot(vector.rgb, vec3(1.0)) / 3.0; +} + +float float_from_vec3(vec3 vector) +{ + return dot(vector, vec3(1.0)) / 3.0; +} + +vec3 vec3_from_vec4(vec4 vector) +{ + return vector.rgb; +} + +vec3 vec3_from_float(float value) +{ + return vec3(value); +} + +vec4 vec4_from_vec3(vec3 vector) +{ + return vec4(vector, 1.0); +} + +vec4 vec4_from_float(float value) +{ + return vec4(vec3(value), 1.0); +} diff --git a/source/blender/imbuf/IMB_colormanagement.h b/source/blender/imbuf/IMB_colormanagement.h index 0818dd653a1..2fb1d814c83 100644 --- a/source/blender/imbuf/IMB_colormanagement.h +++ b/source/blender/imbuf/IMB_colormanagement.h @@ -56,6 +56,8 @@ bool IMB_colormanagement_space_name_is_data(const char *name); bool IMB_colormanagement_space_name_is_scene_linear(const char *name); bool IMB_colormanagement_space_name_is_srgb(const char *name); +BLI_INLINE void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3]); + /** * Convert a float RGB triplet to the correct luminance weighted average. * diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 20c414bb1ad..28125c006eb 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -41,6 +41,7 @@ /* for bool */ #include "../blenlib/BLI_sys_types.h" +#include "../gpu/GPU_texture.h" #ifdef __cplusplus extern "C" { @@ -74,12 +75,6 @@ struct Stereo3dFormat; /** * - * \attention defined in GPU_texture.h - */ -struct GPUTexture; - -/** - * * \attention Defined in allocimbuf.c */ void IMB_init(void); @@ -933,22 +928,25 @@ const char *IMB_ffmpeg_last_error(void); * * \attention defined in util_gpu.c */ -struct GPUTexture *IMB_create_gpu_texture(const char *name, - struct ImBuf *ibuf, - bool use_high_bitdepth, - bool use_premult); +GPUTexture *IMB_create_gpu_texture(const char *name, + struct ImBuf *ibuf, + bool use_high_bitdepth, + bool use_premult); + +eGPUTextureFormat IMB_gpu_get_texture_format(const struct ImBuf *ibuf, bool high_bitdepth); + /** * The `ibuf` is only here to detect the storage type. The produced texture will have undefined * content. It will need to be populated by using #IMB_update_gpu_texture_sub(). */ -struct GPUTexture *IMB_touch_gpu_texture( +GPUTexture *IMB_touch_gpu_texture( const char *name, struct ImBuf *ibuf, int w, int h, int layers, bool use_high_bitdepth); /** * Will update a #GPUTexture using the content of the #ImBuf. Only one layer will be updated. * Will resize the ibuf if needed. * Z is the layer to update. Unused if the texture is 2D. */ -void IMB_update_gpu_texture_sub(struct GPUTexture *tex, +void IMB_update_gpu_texture_sub(GPUTexture *tex, struct ImBuf *ibuf, int x, int y, diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 1b32bef0a98..5ad226a26f2 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -251,11 +251,11 @@ typedef struct ImBuf { int refcounter; /* some parameters to pass along for packing images */ - /** Compressed image only used with png and exr currently */ + /** Compressed image only used with PNG and EXR currently */ unsigned char *encodedbuffer; - /** Size of data written to encodedbuffer */ + /** Size of data written to `encodedbuffer`. */ unsigned int encodedsize; - /** Size of encodedbuffer */ + /** Size of `encodedbuffer` */ unsigned int encodedbuffersize; /* color management */ diff --git a/source/blender/imbuf/intern/colormanagement_inline.c b/source/blender/imbuf/intern/colormanagement_inline.c index 668307ec802..3c6c0f5fd0a 100644 --- a/source/blender/imbuf/intern/colormanagement_inline.c +++ b/source/blender/imbuf/intern/colormanagement_inline.c @@ -11,6 +11,11 @@ #include "BLI_math_vector.h" #include "IMB_colormanagement_intern.h" +void IMB_colormanagement_get_luminance_coefficients(float r_rgb[3]) +{ + copy_v3_v3(r_rgb, imbuf_luma_coefficients); +} + float IMB_colormanagement_get_luminance(const float rgb[3]) { return dot_v3v3(imbuf_luma_coefficients, rgb); diff --git a/source/blender/imbuf/intern/util_gpu.c b/source/blender/imbuf/intern/util_gpu.c index 5feb0ceb515..727704e27e8 100644 --- a/source/blender/imbuf/intern/util_gpu.c +++ b/source/blender/imbuf/intern/util_gpu.c @@ -290,3 +290,13 @@ GPUTexture *IMB_create_gpu_texture(const char *name, return tex; } + +eGPUTextureFormat IMB_gpu_get_texture_format(const ImBuf *ibuf, bool high_bitdepth) +{ + eGPUTextureFormat gpu_texture_format; + eGPUDataFormat gpu_data_format; + + imb_gpu_get_format(ibuf, high_bitdepth, &gpu_data_format, &gpu_texture_format); + + return gpu_texture_format; +} diff --git a/source/blender/io/common/CMakeLists.txt b/source/blender/io/common/CMakeLists.txt index a6818c0bf5d..ee5c6a0a47f 100644 --- a/source/blender/io/common/CMakeLists.txt +++ b/source/blender/io/common/CMakeLists.txt @@ -19,15 +19,15 @@ set(SRC intern/dupli_parent_finder.cc intern/dupli_persistent_id.cc intern/object_identifier.cc - intern/path_util.cc intern/orientation.c + intern/path_util.cc IO_abstract_hierarchy_iterator.h IO_dupli_persistent_id.hh + IO_orientation.h IO_path_util.hh IO_path_util_types.h IO_types.h - IO_orientation.h intern/dupli_parent_finder.hh ) diff --git a/source/blender/io/gpencil/gpencil_io.h b/source/blender/io/gpencil/gpencil_io.h index 215891e3e48..eb811fa2de8 100644 --- a/source/blender/io/gpencil/gpencil_io.h +++ b/source/blender/io/gpencil/gpencil_io.h @@ -35,6 +35,8 @@ typedef struct GpencilIOParams { /** Stroke sampling factor. */ float stroke_sample; int32_t resolution; + /** Filename to be used in new objects. */ + char filename[128]; } GpencilIOParams; /* GpencilIOParams->flag. */ diff --git a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc index 9b00fbaa027..6d4439243fd 100644 --- a/source/blender/io/gpencil/intern/gpencil_io_import_base.cc +++ b/source/blender/io/gpencil/intern/gpencil_io_import_base.cc @@ -14,6 +14,7 @@ #include "BKE_material.h" #include "ED_gpencil.h" +#include "ED_object.h" #include "gpencil_io_import_base.hh" @@ -27,10 +28,22 @@ GpencilImporter::GpencilImporter(const GpencilIOParams *iparams) : GpencilIO(ipa Object *GpencilImporter::create_object() { - const float *cur = scene_->cursor.location; + const float *cur_loc = scene_->cursor.location; + const float rot[3] = {0.0f}; ushort local_view_bits = (params_.v3d && params_.v3d->localvd) ? params_.v3d->local_view_uuid : (ushort)0; - Object *ob_gpencil = ED_gpencil_add_object(params_.C, cur, local_view_bits); + + Object *ob_gpencil = ED_object_add_type(params_.C, + OB_GPENCIL, + (params_.filename[0] != '\0') ? params_.filename : + nullptr, + cur_loc, + rot, + false, + local_view_bits); + + /* Set object defaults. */ + ED_gpencil_add_defaults(params_.C, ob_gpencil); return ob_gpencil; } diff --git a/source/blender/io/stl/CMakeLists.txt b/source/blender/io/stl/CMakeLists.txt index e0c48bbbf7e..3a21da5c579 100644 --- a/source/blender/io/stl/CMakeLists.txt +++ b/source/blender/io/stl/CMakeLists.txt @@ -24,16 +24,16 @@ set(INC_SYS set(SRC IO_stl.cc - importer/stl_import_mesh.cc + importer/stl_import.cc importer/stl_import_ascii_reader.cc importer/stl_import_binary_reader.cc - importer/stl_import.cc + importer/stl_import_mesh.cc IO_stl.h - importer/stl_import_mesh.hh + importer/stl_import.hh importer/stl_import_ascii_reader.hh importer/stl_import_binary_reader.hh - importer/stl_import.hh + importer/stl_import_mesh.hh ) set(LIB diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index 8feceee55ed..f59b8be147e 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -9,8 +9,11 @@ #include "BKE_node.h" #include "BKE_node_tree_update.h" +#include "BLI_fileops.h" #include "BLI_math_vector.h" +#include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_vector.hh" #include "DNA_material_types.h" @@ -94,6 +97,60 @@ static void link_nodes( nodeAddLink(ntree, source, source_socket, dest, dest_socket); } +/* Returns a layer handle retrieved from the given attribute's property specs. + * Note that the returned handle may be invalid if no layer could be found. */ +static pxr::SdfLayerHandle get_layer_handle(const pxr::UsdAttribute &attribute) +{ + for (auto PropertySpec : attribute.GetPropertyStack(pxr::UsdTimeCode::EarliestTime())) { + if (PropertySpec->HasDefaultValue() || + PropertySpec->GetLayer()->GetNumTimeSamplesForPath(PropertySpec->GetPath()) > 0) { + return PropertySpec->GetLayer(); + } + } + + return pxr::SdfLayerHandle(); +} + +static bool is_udim_path(const std::string &path) +{ + return path.find("<UDIM>") != std::string::npos; +} + +/* For the given UDIM path (assumed to contain the UDIM token), returns an array + * containing valid tile indices. */ +static blender::Vector<int> get_udim_tiles(const std::string &file_path) +{ + char base_udim_path[FILE_MAX]; + BLI_strncpy(base_udim_path, file_path.c_str(), sizeof(base_udim_path)); + + blender::Vector<int> udim_tiles; + + /* Extract the tile numbers from all files on disk. */ + ListBase tiles = {nullptr, nullptr}; + int tile_start, tile_range; + bool result = BKE_image_get_tile_info(base_udim_path, &tiles, &tile_start, &tile_range); + if (result) { + LISTBASE_FOREACH (LinkData *, tile, &tiles) { + int tile_number = POINTER_AS_INT(tile->data); + udim_tiles.append(tile_number); + } + } + + BLI_freelistN(&tiles); + + return udim_tiles; +} + +/* Add tiles with the given indices to the given image. */ +static void add_udim_tiles(Image *image, const blender::Vector<int> &indices) +{ + image->source = IMA_SRC_TILED; + + for (int tile_number : indices) { + BKE_image_add_tile(image, tile_number, nullptr); + } +} + /* Returns true if the given shader may have opacity < 1.0, based * on heuristics. */ static bool needs_blend(const pxr::UsdShadeShader &usd_shader) @@ -601,11 +658,31 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, const pxr::SdfAssetPath &asset_path = file_val.Get<pxr::SdfAssetPath>(); std::string file_path = asset_path.GetResolvedPath(); if (file_path.empty()) { + /* No resolved path, so use the asset path (usually + * necessary for UDIM paths). */ + file_path = asset_path.GetAssetPath(); + + /* Texture paths are frequently relative to the USD, so get + * the absolute path. */ + if (pxr::SdfLayerHandle layer_handle = get_layer_handle(file_input.GetAttr())) { + file_path = layer_handle->ComputeAbsolutePath(file_path); + } + } + + if (file_path.empty()) { std::cerr << "WARNING: Couldn't resolve image asset '" << asset_path << "' for Texture Image node." << std::endl; return; } + /* If this is a UDIM texture, this will store the + * UDIM tile indices. */ + blender::Vector<int> udim_tiles; + + if (is_udim_path(file_path)) { + udim_tiles = get_udim_tiles(file_path); + } + const char *im_file = file_path.c_str(); Image *image = BKE_image_load_exists(bmain_, im_file); if (!image) { @@ -614,6 +691,10 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, return; } + if (udim_tiles.size() > 0) { + add_udim_tiles(image, udim_tiles); + } + tex_image->id = &image->id; /* Set texture color space. diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 45657525527..103bb0d0cef 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -64,12 +64,26 @@ static void build_mat_map(const Main *bmain, std::map<std::string, Material *> * static pxr::UsdShadeMaterial compute_bound_material(const pxr::UsdPrim &prim) { - return pxr::UsdShadeMaterialBindingAPI(prim).ComputeBoundMaterial(); + pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(prim); + + /* Compute generically bound ('allPurpose') materials. */ + pxr::UsdShadeMaterial mtl = api.ComputeBoundMaterial(); + + /* If no generic material could be resolved, also check for 'preview' and + * 'full' purpose materials as fallbacks. */ + if (!mtl) { + mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->preview); + } + + if (!mtl) { + mtl = api.ComputeBoundMaterial(pxr::UsdShadeTokens->full); + } + + return mtl; } -/* Returns an existing Blender material that corresponds to the USD - * material with with the given path. Returns null if no such material - * exists. */ +/* Returns an existing Blender material that corresponds to the USD material with the given path. + * Returns null if no such material exists. */ static Material *find_existing_material( const pxr::SdfPath &usd_mat_path, const USDImportParams ¶ms, diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc index fb0b4a1aca9..2fd2973ee73 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.cc +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.cc @@ -4,6 +4,7 @@ * \ingroup obj */ +#include "BLI_path_util.h" #include "BLI_timeit.hh" #include "IO_wavefront_obj.h" @@ -11,14 +12,26 @@ #include "obj_exporter.hh" #include "obj_importer.hh" +using namespace blender::timeit; + +static void report_duration(const char *job, const TimePoint &start_time, const char *path) +{ + Nanoseconds duration = Clock::now() - start_time; + std::cout << "OBJ " << job << " of '" << BLI_path_basename(path) << "' took "; + print_duration(duration); + std::cout << '\n'; +} + void OBJ_export(bContext *C, const OBJExportParams *export_params) { - SCOPED_TIMER("OBJ export"); + TimePoint start_time = Clock::now(); blender::io::obj::exporter_main(C, *export_params); + report_duration("export", start_time, export_params->filepath); } void OBJ_import(bContext *C, const OBJImportParams *import_params) { - SCOPED_TIMER(__func__); + TimePoint start_time = Clock::now(); blender::io::obj::importer_main(C, *import_params); + report_duration("import", start_time, import_params->filepath); } diff --git a/source/blender/io/wavefront_obj/IO_wavefront_obj.h b/source/blender/io/wavefront_obj/IO_wavefront_obj.h index 6ad96083e37..847b02d3fd1 100644 --- a/source/blender/io/wavefront_obj/IO_wavefront_obj.h +++ b/source/blender/io/wavefront_obj/IO_wavefront_obj.h @@ -75,6 +75,7 @@ struct OBJImportParams { bool import_vertex_groups; bool validate_meshes; bool relative_paths; + bool clear_selection; }; /** diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index 9460746630d..9b050af0891 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -296,7 +296,7 @@ void OBJMesh::store_uv_coords_and_indices() const float limit[2] = {STD_UV_CONNECT_LIMIT, STD_UV_CONNECT_LIMIT}; UvVertMap *uv_vert_map = BKE_mesh_uv_vert_map_create( - mpoly, mloop, mloopuv, totpoly, totvert, limit, false, false); + mpoly, nullptr, mloop, mloopuv, totpoly, totvert, limit, false, false); uv_indices_.resize(totpoly); /* At least total vertices of a mesh will be present in its texture map. So diff --git a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc index 9a457167fca..7e282b164b0 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_string_utils.cc @@ -41,12 +41,14 @@ void fixup_line_continuations(char *p, char *end) while (true) { /* Find next backslash, if any. */ char *backslash = std::find(p, end, '\\'); - if (backslash == end) + if (backslash == end) { break; + } /* Skip over possible whitespace right after it. */ p = backslash + 1; - while (p < end && is_whitespace(*p) && *p != '\n') + while (p < end && is_whitespace(*p) && *p != '\n') { ++p; + } /* If then we have a newline, turn both backslash * and the newline into regular spaces. */ if (p < end && *p == '\n') { diff --git a/source/blender/io/wavefront_obj/importer/obj_importer.cc b/source/blender/io/wavefront_obj/importer/obj_importer.cc index bb32776d2be..5d3f75e7f38 100644 --- a/source/blender/io/wavefront_obj/importer/obj_importer.cc +++ b/source/blender/io/wavefront_obj/importer/obj_importer.cc @@ -39,7 +39,6 @@ static void geometry_to_blender_objects(Main *bmain, Map<std::string, std::unique_ptr<MTLMaterial>> &materials, Map<std::string, Material *> &created_materials) { - BKE_view_layer_base_deselect_all(view_layer); LayerCollection *lc = BKE_layer_collection_get_active(view_layer); /* Don't do collection syncs for each object, will do once after the loop. */ @@ -122,6 +121,9 @@ void importer_main(Main *bmain, mtl_parser.parse_and_store(materials); } + if (import_params.clear_selection) { + BKE_view_layer_base_deselect_all(view_layer); + } geometry_to_blender_objects(bmain, scene, view_layer, diff --git a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc index f97546fe4f5..132bb03357f 100644 --- a/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc +++ b/source/blender/io/wavefront_obj/tests/obj_importer_tests.cc @@ -62,6 +62,7 @@ class obj_importer_test : public BlendfileLoadingBaseTest { params.validate_meshes = true; params.import_vertex_groups = false; params.relative_paths = true; + params.clear_selection = true; std::string obj_path = blender::tests::flags_test_asset_dir() + "/io_tests/obj/" + path; strncpy(params.filepath, obj_path.c_str(), FILE_MAX - 1); diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index a77b7034241..b3a07f7ff37 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -37,7 +37,7 @@ typedef struct DrawData { /* Only nested data, NOT the engine data itself. */ DrawDataFreeCb free; /* Accumulated recalc flags, which corresponds to ID->recalc flags. */ - int recalc; + unsigned int recalc; } DrawData; typedef struct DrawDataList { @@ -256,6 +256,7 @@ typedef struct IDOverrideLibraryProperty { /** * List of overriding operations (IDOverrideLibraryPropertyOperation) applied to this property. + * Recreated as part of the diffing, so do not store any of these elsewhere. */ ListBase operations; @@ -387,7 +388,7 @@ typedef struct ID { int tag; int us; int icon_id; - int recalc; + unsigned int recalc; /** * Used by undo code. recalc_after_undo_push contains the changes between the * last undo push and the current state. This is accumulated as IDs are tagged @@ -397,8 +398,8 @@ typedef struct ID { * recalc_after_undo_push at the time of the undo push. This means it can be * used to find the changes between undo states. */ - int recalc_up_to_undo_push; - int recalc_after_undo_push; + unsigned int recalc_up_to_undo_push; + unsigned int recalc_after_undo_push; /** * A session-wide unique identifier for a given ID, that remain the same across potential @@ -870,6 +871,17 @@ typedef enum IDRecalcFlag { /* The node tree has changed in a way that affects its output nodes. */ ID_RECALC_NTREE_OUTPUT = (1 << 25), + /* Provisioned flags. + * + * Not for actual use. The idea of them is to have all bits of the `IDRecalcFlag` defined to a + * known value, silencing sanitizer warnings when checking bits of the ID_RECALC_ALL. */ + ID_RECALC_PROVISION_26 = (1 << 26), + ID_RECALC_PROVISION_27 = (1 << 27), + ID_RECALC_PROVISION_28 = (1 << 28), + ID_RECALC_PROVISION_29 = (1 << 29), + ID_RECALC_PROVISION_30 = (1 << 30), + ID_RECALC_PROVISION_31 = (1u << 31), + /*************************************************************************** * Pseudonyms, to have more semantic meaning in the actual code without * using too much low-level and implementation specific tags. */ @@ -888,7 +900,8 @@ typedef enum IDRecalcFlag { * Do NOT use those for tagging. */ /* Identifies that SOMETHING has been changed in this ID. */ - ID_RECALC_ALL = ~(0), + ID_RECALC_ALL = (0xffffffff), + /* Identifies that something in particle system did change. */ ID_RECALC_PSYS_ALL = (ID_RECALC_PSYS_REDO | ID_RECALC_PSYS_RESET | ID_RECALC_PSYS_CHILD | ID_RECALC_PSYS_PHYS), diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index d49d0906aa7..29795519719 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -96,7 +96,7 @@ typedef enum eAssetLibraryType { } eAssetLibraryType; /** - * Information to identify a asset library. May be either one of the predefined types (current + * Information to identify an asset library. May be either one of the predefined types (current * 'Main', builtin library, project library), or a custom type as defined in the Preferences. * * If the type is set to #ASSET_LIBRARY_CUSTOM, `custom_library_index` must be set to identify the diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 6e4e515a0fe..f35c77f663b 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -92,8 +92,14 @@ typedef struct ImageTile { struct ImageTile_Runtime runtime; - char _pad[4]; int tile_number; + + /* for generated images */ + int gen_x, gen_y; + char gen_type, gen_flag; + short gen_depth; + float gen_color[4]; + char label[64]; } ImageTile; @@ -167,10 +173,10 @@ typedef struct Image { int lastused; /* for generated images */ - int gen_x, gen_y; - char gen_type, gen_flag; - short gen_depth; - float gen_color[4]; + int gen_x DNA_DEPRECATED, gen_y DNA_DEPRECATED; + char gen_type DNA_DEPRECATED, gen_flag DNA_DEPRECATED; + short gen_depth DNA_DEPRECATED; + float gen_color[4] DNA_DEPRECATED; /* display aspect - for UV editing images resized for faster openGL display */ float aspx, aspy; @@ -262,7 +268,8 @@ enum { /** #Image.gen_flag */ enum { - IMA_GEN_FLOAT = 1, + IMA_GEN_FLOAT = (1 << 0), + IMA_GEN_TILE = (1 << 1), }; /** #Image.alpha_mode */ diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 0af50b2bd4f..345fa141514 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -37,7 +37,7 @@ typedef enum eViewLayerEEVEEPassType { EEVEE_RENDER_PASS_CRYPTOMATTE = (1 << 16), EEVEE_RENDER_PASS_VECTOR = (1 << 17), } eViewLayerEEVEEPassType; -#define EEVEE_RENDER_PASS_MAX_BIT 17 +#define EEVEE_RENDER_PASS_MAX_BIT 18 /* #ViewLayerAOV.type */ typedef enum eViewLayerAOVType { diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 2a4234bde6a..c62907e26ed 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -30,10 +30,14 @@ typedef struct MVert { } MVert; /** #MVert.flag */ + +#ifdef DNA_DEPRECATED_ALLOW enum { /* SELECT = (1 << 0), */ + /** Deprecated hide status. Now stored in ".hide_vert" attribute. */ ME_HIDE = (1 << 4), }; +#endif /** * Mesh Edges. @@ -52,10 +56,10 @@ enum { /* SELECT = (1 << 0), */ ME_EDGEDRAW = (1 << 1), ME_SEAM = (1 << 2), + /** Deprecated hide status. Now stored in ".hide_edge" attribute. */ /* ME_HIDE = (1 << 4), */ ME_EDGERENDER = (1 << 5), ME_LOOSEEDGE = (1 << 7), - ME_EDGE_TMP_TAG = (1 << 8), ME_SHARP = (1 << 9), /* only reason this flag remains a 'short' */ }; @@ -78,6 +82,7 @@ typedef struct MPoly { enum { ME_SMOOTH = (1 << 0), ME_FACE_SEL = (1 << 1), + /** Deprecated hide status. Now stored in ".hide_poly" attribute. */ /* ME_HIDE = (1 << 4), */ }; @@ -352,7 +357,7 @@ typedef struct MDisps { /** * Used for hiding parts of a multires mesh. - * Essentially the multires equivalent of #MVert.flag's ME_HIDE bit. + * Essentially the multires equivalent of the mesh ".hide_vert" boolean layer. * * \note This is a bitmap, keep in sync with type used in BLI_bitmap.h */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index f148116eba8..787f52f9891 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -2217,7 +2217,7 @@ typedef struct SurfaceDeformModifierData { SDefVert *verts; void *_pad1; float falloff; - /* Number of of vertices on the deformed mesh upon the bind process. */ + /* Number 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; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 76d8207eead..b9161e918c0 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -632,12 +632,12 @@ typedef struct bNodeSocketValueMaterial { /* Data structs, for `node->storage`. */ -enum { +typedef enum CMPNodeMaskType { CMP_NODE_MASKTYPE_ADD = 0, CMP_NODE_MASKTYPE_SUBTRACT = 1, CMP_NODE_MASKTYPE_MULTIPLY = 2, CMP_NODE_MASKTYPE_NOT = 3, -}; +} CMPNodeMaskType; enum { CMP_NODE_DILATEERODE_STEP = 0, @@ -1838,6 +1838,49 @@ enum { /* viewer and composite output. */ #define CMP_NODE_OUTPUT_IGNORE_ALPHA 1 +/** Split Viewer Node. Stored in `custom2`. */ +typedef enum CMPNodeSplitViewerAxis { + CMP_NODE_SPLIT_VIEWER_HORIZONTAL = 0, + CMP_NODE_SPLIT_VIEWER_VERTICAL = 1, +} CMPNodeSplitViewerAxis; + +/** Color Balance Node. Stored in `custom1`. */ +typedef enum CMPNodeColorBalanceMethod { + CMP_NODE_COLOR_BALANCE_LGG = 0, + CMP_NODE_COLOR_BALANCE_ASC_CDL = 1, +} CMPNodeColorBalanceMethod; + +/** Alpha Convert Node. Stored in `custom1`. */ +typedef enum CMPNodeAlphaConvertMode { + CMP_NODE_ALPHA_CONVERT_PREMULTIPLY = 0, + CMP_NODE_ALPHA_CONVERT_UNPREMULTIPLY = 1, +} CMPNodeAlphaConvertMode; + +/** Distance Matte Node. Stored in #NodeChroma.channel. */ +typedef enum CMPNodeDistanceMatteColorSpace { + CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_YCCA = 0, + CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA = 1, +} CMPNodeDistanceMatteColorSpace; + +/** Color Spill Node. Stored in `custom2`. */ +typedef enum CMPNodeColorSpillLimitAlgorithm { + CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE = 0, + CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE = 1, +} CMPNodeColorSpillLimitAlgorithm; + +/** Channel Matte Node. Stored in #NodeChroma.algorithm. */ +typedef enum CMPNodeChannelMatteLimitAlgorithm { + CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_SINGLE = 0, + CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX = 1, +} CMPNodeChannelMatteLimitAlgorithm; + +/* Flip Node. Stored in custom1. */ +typedef enum CMPNodeFlipMode { + CMP_NODE_FLIP_X = 0, + CMP_NODE_FLIP_Y = 1, + CMP_NODE_FLIP_X_Y = 2, +} CMPNodeFlipMode; + /* Plane track deform node. */ enum { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f8fcd78d63b..2c3c16393e0 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -9,7 +9,7 @@ #include "DNA_defs.h" -/* XXX(campbell): temp feature. */ +/* XXX(@campbellbarton): temp feature. */ #define DURIAN_CAMERA_SWITCH /* check for cyclic set-scene, @@ -2209,7 +2209,7 @@ enum { OB_DRAW_GROUPUSER_ALL = 2, }; -/* object_vgroup.c */ +/* object_vgroup.cc */ /** #ToolSettings.vgroupsubset */ typedef enum eVGroupSelect { diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 74fb1c3ac96..ac553c2655f 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -653,6 +653,8 @@ typedef struct UserDef_Experimental { char enable_eevee_next; char use_sculpt_texture_paint; char use_draw_manager_acquire_lock; + char use_realtime_compositor; + char _pad[7]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 8554d070dc3..0d281032b7e 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -486,6 +486,7 @@ enum { V3D_SHADING_SCENE_LIGHTS_RENDER = (1 << 12), V3D_SHADING_SCENE_WORLD_RENDER = (1 << 13), V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION = (1 << 14), + V3D_SHADING_COMPOSITOR = (1 << 15), }; #define V3D_USES_SCENE_LIGHTS(v3d) \ diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 242bfd99eae..c59e45e1b01 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -654,7 +654,7 @@ static ID *rna_ID_evaluated_get(ID *id, struct Depsgraph *depsgraph) static ID *rna_ID_copy(ID *id, Main *bmain) { - ID *newid = BKE_id_copy(bmain, id); + ID *newid = BKE_id_copy_for_use_in_bmain(bmain, id); if (newid != NULL) { id_us_min(newid); @@ -2067,7 +2067,10 @@ static void rna_def_ID(BlenderRNA *brna) func = RNA_def_function(srna, "copy", "rna_ID_copy"); RNA_def_function_ui_description( - func, "Create a copy of this data-block (not supported for all data-blocks)"); + func, + "Create a copy of this data-block (not supported for all data-blocks). " + "The result is added to the Blend-File Data (Main database), with all references to other " + "data-blocks ensured to be from within the same Blend-File Data"); RNA_def_function_flag(func, FUNC_USE_MAIN); parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID"); RNA_def_function_return(func, parm); @@ -2177,7 +2180,7 @@ static void rna_def_ID(BlenderRNA *brna) func = RNA_def_function(srna, "animation_data_clear", "rna_ID_animation_data_free"); RNA_def_function_flag(func, FUNC_USE_MAIN); - RNA_def_function_ui_description(func, "Clear animation on this this ID"); + RNA_def_function_ui_description(func, "Clear animation on this ID"); func = RNA_def_function(srna, "update_tag", "rna_ID_update_tag"); RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 04707c01d6b..ada73157026 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -2073,7 +2073,7 @@ static void rna_property_update( } #if 1 - /* TODO(campbell): Should eventually be replaced entirely by message bus (below) + /* TODO(@campbellbarton): Should eventually be replaced entirely by message bus (below) * for now keep since COW, bugs are hard to track when we have other missing updates. */ if (prop->noteflag) { WM_main_add_notifier(prop->noteflag, ptr->owner_id); diff --git a/source/blender/makesrna/intern/rna_action.c b/source/blender/makesrna/intern/rna_action.c index bcfb646ca19..14f4a82c62b 100644 --- a/source/blender/makesrna/intern/rna_action.c +++ b/source/blender/makesrna/intern/rna_action.c @@ -177,7 +177,7 @@ static void rna_Action_fcurve_clear(bAction *act) static TimeMarker *rna_Action_pose_markers_new(bAction *act, const char name[]) { TimeMarker *marker = MEM_callocN(sizeof(TimeMarker), "TimeMarker"); - marker->flag = 1; + marker->flag = SELECT; marker->frame = 1; BLI_strncpy_utf8(marker->name, name, sizeof(marker->name)); BLI_addtail(&act->markers, marker); diff --git a/source/blender/makesrna/intern/rna_curves.c b/source/blender/makesrna/intern/rna_curves.c index cb8b36f41d2..17290d1c582 100644 --- a/source/blender/makesrna/intern/rna_curves.c +++ b/source/blender/makesrna/intern/rna_curves.c @@ -195,7 +195,7 @@ static void rna_def_curves_point(BlenderRNA *brna) PropertyRNA *prop; srna = RNA_def_struct(brna, "CurvePoint", NULL); - RNA_def_struct_ui_text(srna, "Curve Point", "Curve curve control point"); + RNA_def_struct_ui_text(srna, "Curve Point", "Curve control point"); RNA_def_struct_path_func(srna, "rna_CurvePoint_path"); prop = RNA_def_property(srna, "position", PROP_FLOAT, PROP_TRANSLATION); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 7f134c5055f..b7ab7689dd7 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -52,6 +52,7 @@ static const EnumPropertyItem image_source_items[] = { #ifdef RNA_RUNTIME # include "BLI_math_base.h" +# include "BLI_math_vector.h" # include "BKE_global.h" @@ -85,6 +86,10 @@ static void rna_Image_source_set(PointerRNA *ptr, int value) ima->source = value; BLI_assert(BKE_id_is_in_global_main(&ima->id)); BKE_image_signal(G_MAIN, ima, NULL, IMA_SIGNAL_SRC_CHANGE); + if (ima->source == IMA_SRC_TILED) { + BKE_image_signal(G_MAIN, ima, NULL, IMA_SIGNAL_RELOAD); + } + DEG_id_tag_update(&ima->id, 0); DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS); DEG_relations_tag_update(G_MAIN); @@ -100,6 +105,83 @@ static void rna_Image_reload_update(Main *bmain, Scene *UNUSED(scene), PointerRN DEG_id_tag_update(&ima->id, ID_RECALC_EDITORS); } +static int rna_Image_generated_type_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + return base_tile->gen_type; +} + +static void rna_Image_generated_type_set(PointerRNA *ptr, int value) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_type = value; +} + +static int rna_Image_generated_width_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + return base_tile->gen_x; +} + +static void rna_Image_generated_width_set(PointerRNA *ptr, int value) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_x = CLAMPIS(value, 1, 65536); +} + +static int rna_Image_generated_height_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + return base_tile->gen_y; +} + +static void rna_Image_generated_height_set(PointerRNA *ptr, int value) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + base_tile->gen_y = CLAMPIS(value, 1, 65536); +} + +static bool rna_Image_generated_float_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + return (base_tile->gen_flag & IMA_GEN_FLOAT) != 0; +} + +static void rna_Image_generated_float_set(PointerRNA *ptr, bool value) +{ + Image *ima = (Image *)ptr->data; + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + if (value) { + base_tile->gen_flag |= IMA_GEN_FLOAT; + } + else { + base_tile->gen_flag &= ~IMA_GEN_FLOAT; + } +} + +void rna_Image_generated_color_get(PointerRNA *ptr, float values[4]) +{ + Image *ima = (Image *)(ptr->data); + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + copy_v4_v4(values, base_tile->gen_color); +} + +void rna_Image_generated_color_set(PointerRNA *ptr, const float values[4]) +{ + Image *ima = (Image *)(ptr->data); + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + for (unsigned int i = 0; i < 4; i++) { + base_tile->gen_color[i] = CLAMPIS(values[i], 0.0f, FLT_MAX); + } +} + static void rna_Image_generated_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr) { Image *ima = (Image *)ptr->owner_id; @@ -335,6 +417,20 @@ static void rna_UDIMTile_tile_number_set(PointerRNA *ptr, int value) } } +static void rna_UDIMTile_generated_update(Main *UNUSED(bmain), + Scene *UNUSED(scene), + PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->owner_id; + ImageTile *tile = (ImageTile *)ptr->data; + + /* If the tile is still marked as generated, then update the tile as requested. */ + if ((tile->gen_flag & IMA_GEN_TILE) != 0) { + BKE_image_fill_tile(ima, tile); + BKE_image_partial_update_mark_full_update(ima); + } +} + static int rna_Image_active_tile_index_get(PointerRNA *ptr) { Image *image = (Image *)ptr->data; @@ -896,6 +992,43 @@ static void rna_def_udim_tile(BlenderRNA *brna) RNA_def_property_int_funcs(prop, "rna_UDIMTile_channels_get", NULL, NULL); RNA_def_property_ui_text(prop, "Channels", "Number of channels in the tile pixels buffer"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + /* Generated tile information. */ + prop = RNA_def_property(srna, "generated_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "gen_type"); + RNA_def_property_enum_items(prop, rna_enum_image_generated_type_items); + RNA_def_property_ui_text(prop, "Generated Type", "Generated image type"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "generated_width", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "gen_x"); + RNA_def_property_flag(prop, PROP_PROPORTIONAL); + RNA_def_property_range(prop, 1, 65536); + RNA_def_property_ui_text(prop, "Generated Width", "Generated image width"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "generated_height", PROP_INT, PROP_PIXEL); + RNA_def_property_int_sdna(prop, NULL, "gen_y"); + RNA_def_property_flag(prop, PROP_PROPORTIONAL); + RNA_def_property_range(prop, 1, 65536); + RNA_def_property_ui_text(prop, "Generated Height", "Generated image height"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "use_generated_float", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "gen_flag", IMA_GEN_FLOAT); + RNA_def_property_ui_text(prop, "Float Buffer", "Generate floating-point buffer"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + + prop = RNA_def_property(srna, "generated_color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "gen_color"); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Color", "Fill color for the generated image"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_UDIMTile_generated_update"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); } static void rna_def_udim_tiles(BlenderRNA *brna, PropertyRNA *cprop) @@ -1079,6 +1212,8 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_enum_sdna(prop, NULL, "gen_type"); RNA_def_property_enum_items(prop, rna_enum_image_generated_type_items); RNA_def_property_ui_text(prop, "Generated Type", "Generated image type"); + RNA_def_property_enum_funcs( + prop, "rna_Image_generated_type_get", "rna_Image_generated_type_set", NULL); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1087,6 +1222,8 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_range(prop, 1, 65536); RNA_def_property_ui_text(prop, "Generated Width", "Generated image width"); + RNA_def_property_int_funcs( + prop, "rna_Image_generated_width_get", "rna_Image_generated_width_set", NULL); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1095,12 +1232,16 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_PROPORTIONAL); RNA_def_property_range(prop, 1, 65536); RNA_def_property_ui_text(prop, "Generated Height", "Generated image height"); + RNA_def_property_int_funcs( + prop, "rna_Image_generated_height_get", "rna_Image_generated_height_set", NULL); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); prop = RNA_def_property(srna, "use_generated_float", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "gen_flag", IMA_GEN_FLOAT); RNA_def_property_ui_text(prop, "Float Buffer", "Generate floating-point buffer"); + RNA_def_property_boolean_funcs( + prop, "rna_Image_generated_float_get", "rna_Image_generated_float_set"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -1108,6 +1249,8 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_float_sdna(prop, NULL, "gen_color"); RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Color", "Fill color for the generated image"); + RNA_def_property_float_funcs( + prop, "rna_Image_generated_color_get", "rna_Image_generated_color_set", NULL); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_generated_update"); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 65468977ccb..9580c1178ec 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -344,16 +344,88 @@ static void rna_Mesh_update_positions_tag(Main *bmain, Scene *scene, PointerRNA /** \name Property get/set Callbacks * \{ */ +static int rna_MeshVertex_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MVert *vert = (MVert *)ptr->data; + const int index = (int)(vert - mesh->mvert); + BLI_assert(index >= 0); + BLI_assert(index < mesh->totvert); + return index; +} + +static int rna_MeshEdge_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MEdge *edge = (MEdge *)ptr->data; + const int index = (int)(edge - mesh->medge); + BLI_assert(index >= 0); + BLI_assert(index < mesh->totedge); + return index; +} + +static int rna_MeshPolygon_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MPoly *mpoly = (MPoly *)ptr->data; + const int index = (int)(mpoly - mesh->mpoly); + BLI_assert(index >= 0); + BLI_assert(index < mesh->totpoly); + return index; +} + +static int rna_MeshLoop_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MLoop *mloop = (MLoop *)ptr->data; + const int index = (int)(mloop - mesh->mloop); + BLI_assert(index >= 0); + BLI_assert(index < mesh->totloop); + return index; +} + +static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const MLoopTri *ltri = (MLoopTri *)ptr->data; + const int index = (int)(ltri - mesh->runtime.looptris.array); + BLI_assert(index >= 0); + BLI_assert(index < mesh->runtime.looptris.len); + return index; +} + static void rna_MeshVertex_normal_get(PointerRNA *ptr, float *value) { Mesh *mesh = rna_mesh(ptr); const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(mesh); + const int index = rna_MeshVertex_index_get(ptr); + copy_v3_v3(value, vert_normals[index]); +} - const int index = (MVert *)ptr->data - mesh->mvert; - BLI_assert(index >= 0); - BLI_assert(index < mesh->totvert); +static bool rna_MeshVertex_hide_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const bool *hide_vert = (const bool *)CustomData_get_layer_named( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert"); + const int index = rna_MeshVertex_index_get(ptr); + return hide_vert == NULL ? false : hide_vert[index]; +} - copy_v3_v3(value, vert_normals[index]); +static void rna_MeshVertex_hide_set(PointerRNA *ptr, bool value) +{ + Mesh *mesh = rna_mesh(ptr); + bool *hide_vert = (bool *)CustomData_duplicate_referenced_layer_named( + &mesh->vdata, CD_PROP_BOOL, ".hide_vert", mesh->totvert); + if (!hide_vert) { + if (!value) { + /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */ + return; + } + hide_vert = (bool *)CustomData_add_layer_named( + &mesh->vdata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totvert, ".hide_vert"); + } + const int index = rna_MeshVertex_index_get(ptr); + hide_vert[index] = value; } static float rna_MeshVertex_bevel_weight_get(PointerRNA *ptr) @@ -464,6 +536,32 @@ static void rna_MeshPolygon_normal_get(PointerRNA *ptr, float *values) BKE_mesh_calc_poly_normal(mp, me->mloop + mp->loopstart, me->mvert, values); } +static bool rna_MeshPolygon_hide_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const bool *hide_poly = (const bool *)CustomData_get_layer_named( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly"); + const int index = rna_MeshPolygon_index_get(ptr); + return hide_poly == NULL ? false : hide_poly[index]; +} + +static void rna_MeshPolygon_hide_set(PointerRNA *ptr, bool value) +{ + Mesh *mesh = rna_mesh(ptr); + bool *hide_poly = (bool *)CustomData_duplicate_referenced_layer_named( + &mesh->pdata, CD_PROP_BOOL, ".hide_poly", mesh->totpoly); + if (!hide_poly) { + if (!value) { + /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */ + return; + } + hide_poly = (bool *)CustomData_add_layer_named( + &mesh->pdata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totpoly, ".hide_poly"); + } + const int index = rna_MeshPolygon_index_get(ptr); + hide_poly[index] = value; +} + static void rna_MeshPolygon_center_get(PointerRNA *ptr, float *values) { Mesh *me = rna_mesh(ptr); @@ -1174,55 +1272,46 @@ static void rna_MeshPoly_material_index_range( } # endif -static int rna_MeshVertex_index_get(PointerRNA *ptr) -{ - Mesh *me = rna_mesh(ptr); - MVert *vert = (MVert *)ptr->data; - return (int)(vert - me->mvert); -} - -static int rna_MeshEdge_index_get(PointerRNA *ptr) +static bool rna_MeshEdge_hide_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MEdge *edge = (MEdge *)ptr->data; - return (int)(edge - me->medge); + const Mesh *mesh = rna_mesh(ptr); + const bool *hide_edge = (const bool *)CustomData_get_layer_named( + &mesh->edata, CD_PROP_BOOL, ".hide_edge"); + const int index = rna_MeshEdge_index_get(ptr); + return hide_edge == NULL ? false : hide_edge[index]; } -static int rna_MeshLoopTriangle_index_get(PointerRNA *ptr) +static void rna_MeshEdge_hide_set(PointerRNA *ptr, bool value) { - Mesh *me = rna_mesh(ptr); - MLoopTri *ltri = (MLoopTri *)ptr->data; - return (int)(ltri - me->runtime.looptris.array); + Mesh *mesh = rna_mesh(ptr); + bool *hide_edge = (bool *)CustomData_duplicate_referenced_layer_named( + &mesh->edata, CD_PROP_BOOL, ".hide_edge", mesh->totedge); + if (!hide_edge) { + if (!value) { + /* Skip adding layer if it doesn't exist already anyway and we're not hiding an element. */ + return; + } + hide_edge = (bool *)CustomData_add_layer_named( + &mesh->edata, CD_PROP_BOOL, CD_CALLOC, NULL, mesh->totedge, ".hide_edge"); + } + const int index = rna_MeshEdge_index_get(ptr); + hide_edge[index] = value; } static int rna_MeshLoopTriangle_material_index_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MLoopTri *ltri = (MLoopTri *)ptr->data; + const Mesh *me = rna_mesh(ptr); + const MLoopTri *ltri = (MLoopTri *)ptr->data; return me->mpoly[ltri->poly].mat_nr; } static bool rna_MeshLoopTriangle_use_smooth_get(PointerRNA *ptr) { - Mesh *me = rna_mesh(ptr); - MLoopTri *ltri = (MLoopTri *)ptr->data; + const Mesh *me = rna_mesh(ptr); + const MLoopTri *ltri = (MLoopTri *)ptr->data; return me->mpoly[ltri->poly].flag & ME_SMOOTH; } -static int rna_MeshPolygon_index_get(PointerRNA *ptr) -{ - Mesh *me = rna_mesh(ptr); - MPoly *mpoly = (MPoly *)ptr->data; - return (int)(mpoly - me->mpoly); -} - -static int rna_MeshLoop_index_get(PointerRNA *ptr) -{ - Mesh *me = rna_mesh(ptr); - MLoop *mloop = (MLoop *)ptr->data; - return (int)(mloop - me->mloop); -} - /* path construction */ static char *rna_VertexGroupElement_path(const PointerRNA *ptr) @@ -1608,9 +1697,7 @@ static void rna_Mesh_vertex_color_remove(struct Mesh *me, ReportList *reports, CustomDataLayer *layer) { - if (ED_mesh_color_remove_named(me, layer->name) == false) { - BKE_reportf(reports, RPT_ERROR, "Vertex color '%s' not found", layer->name); - } + BKE_id_attribute_remove(&me->id, layer->name, reports); } static PointerRNA rna_Mesh_sculpt_vertex_color_new(struct Mesh *me, @@ -1621,7 +1708,7 @@ static PointerRNA rna_Mesh_sculpt_vertex_color_new(struct Mesh *me, PointerRNA ptr; CustomData *vdata; CustomDataLayer *cdl = NULL; - int index = ED_mesh_sculpt_color_add(me, name, false, do_init, reports); + int index = ED_mesh_sculpt_color_add(me, name, do_init, reports); if (index != -1) { vdata = rna_mesh_vdata_helper(me); @@ -1636,9 +1723,7 @@ static void rna_Mesh_sculpt_vertex_color_remove(struct Mesh *me, ReportList *reports, CustomDataLayer *layer) { - if (ED_mesh_sculpt_color_remove_named(me, layer->name) == false) { - BKE_reportf(reports, RPT_ERROR, "Sculpt vertex color '%s' not found", layer->name); - } + BKE_id_attribute_remove(&me->id, layer->name, reports); } # define DEFINE_CUSTOMDATA_PROPERTY_API( \ @@ -1834,8 +1919,8 @@ static void rna_def_mvert(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE); RNA_def_property_ui_text(prop, "Hide", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshVertex_hide_get", "rna_MeshVertex_hide_set"); RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "bevel_weight", PROP_FLOAT, PROP_NONE); @@ -1910,8 +1995,8 @@ static void rna_def_medge(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE); RNA_def_property_ui_text(prop, "Hide", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshEdge_hide_get", "rna_MeshEdge_hide_set"); RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "use_seam", PROP_BOOLEAN, PROP_NONE); @@ -2123,8 +2208,8 @@ static void rna_def_mpolygon(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "hide", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_HIDE); RNA_def_property_ui_text(prop, "Hide", ""); + RNA_def_property_boolean_funcs(prop, "rna_MeshPolygon_hide_get", "rna_MeshPolygon_hide_set"); RNA_def_property_update(prop, 0, "rna_Mesh_update_select"); prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 24ba8f0fd30..7d2fa8022dd 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -6477,7 +6477,7 @@ static void def_sh_script(StructRNA *srna) RNA_def_function_return(func, parm); func = RNA_def_function(srna, "add_socket", "rna_ShaderNodeScript_add_socket"); - RNA_def_function_ui_description(func, "Add a socket socket"); + RNA_def_function_ui_description(func, "Add a socket"); RNA_def_function_flag(func, FUNC_USE_SELF_ID); parm = RNA_def_string(func, "name", NULL, 0, "Name", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); @@ -6488,7 +6488,7 @@ static void def_sh_script(StructRNA *srna) RNA_def_function_return(func, parm); func = RNA_def_function(srna, "remove_socket", "rna_ShaderNodeScript_remove_socket"); - RNA_def_function_ui_description(func, "Remove a socket socket"); + RNA_def_function_ui_description(func, "Remove a socket"); RNA_def_function_flag(func, FUNC_USE_SELF_ID); parm = RNA_def_pointer(func, "sock", "NodeSocket", "Socket", ""); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED); diff --git a/source/blender/makesrna/intern/rna_path.cc b/source/blender/makesrna/intern/rna_path.cc index 0997ad6ee2f..f14e2113e13 100644 --- a/source/blender/makesrna/intern/rna_path.cc +++ b/source/blender/makesrna/intern/rna_path.cc @@ -380,9 +380,7 @@ static bool rna_path_parse(const PointerRNA *ptr, } const bool use_id_prop = (*path == '['); - /* custom property lookup ? - * C.object["someprop"] - */ + /* Custom property lookup: e.g. `C.object["someprop"]`. */ if (!curptr.data) { return false; @@ -706,7 +704,7 @@ const char *RNA_path_array_index_token_find(const char *rna_path, const Property /* Valid 'array part' of a rna path can only have '[', ']' and digit characters. * It may have more than one of those (e.g. `[12][1]`) in case of multi-dimensional arrays. */ - size_t rna_path_len = (size_t)strlen(rna_path); + int64_t rna_path_len = (int64_t)strlen(rna_path); if (rna_path[rna_path_len] != ']') { return NULL; } @@ -809,7 +807,7 @@ static char *rna_idp_path(PointerRNA *ptr, link.name = NULL; link.index = -1; - for (i = 0, iter = reinterpret_cast<IDProperty *>(haystack->data.group.first); iter; + for (i = 0, iter = static_cast<IDProperty *>(haystack->data.group.first); iter; iter = iter->next, i++) { if (needle == iter) { /* found! */ link.name = iter->name; @@ -911,7 +909,7 @@ static char *rna_path_from_ID_to_idpgroup(const PointerRNA *ptr) */ RNA_id_pointer_create(ptr->owner_id, &id_ptr); - return RNA_path_from_struct_to_idproperty(&id_ptr, reinterpret_cast<IDProperty *>(ptr->data)); + return RNA_path_from_struct_to_idproperty(&id_ptr, static_cast<IDProperty *>(ptr->data)); } ID *RNA_find_real_ID_and_path(Main *bmain, ID *id, const char **r_path) diff --git a/source/blender/makesrna/intern/rna_pose.c b/source/blender/makesrna/intern/rna_pose.c index 30df8e20e8d..e1a46b01db2 100644 --- a/source/blender/makesrna/intern/rna_pose.c +++ b/source/blender/makesrna/intern/rna_pose.c @@ -600,7 +600,7 @@ static void rna_PoseChannel_constraints_remove( ED_object_constraint_update(bmain, ob); - /* XXX(Campbell): is this really needed? */ + /* XXX(@campbellbarton): is this really needed? */ BKE_constraints_active_set(&pchan->constraints, NULL); WM_main_add_notifier(NC_OBJECT | ND_CONSTRAINT | NA_REMOVED, id); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 8ed07a8dbf7..2e78cc97099 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -1295,7 +1295,7 @@ static const EnumPropertyItem *rna_ImageFormatSettings_color_mode_itemf(bContext ID *id = ptr->owner_id; const bool is_render = (id && GS(id->name) == ID_SCE); - /* NOTE(campbell): we need to act differently for render + /* NOTE(@campbellbarton): we need to act differently for render * where 'BW' will force grayscale even if the output format writes * as RGBA, this is age old blender convention and not sure how useful * it really is but keep it for now. */ diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 2e1fa8db7fe..4cd0b27c772 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -1040,7 +1040,7 @@ static void rna_def_paint_mode(BlenderRNA *brna) RNA_def_property_pointer_funcs( prop, NULL, NULL, NULL, "rna_PaintModeSettings_canvas_image_poll"); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE); - RNA_def_property_ui_text(prop, "Texture", "Image used as as painting target"); + RNA_def_property_ui_text(prop, "Texture", "Image used as painting target"); } static void rna_def_image_paint(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 5cee2ca00a3..502b0404764 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -4199,6 +4199,14 @@ static void rna_def_space_view3d_shading(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Shader AOV Name", "Name of the active Shader AOV"); RNA_def_property_flag(prop, PROP_HIDDEN); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "use_compositor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", V3D_SHADING_COMPOSITOR); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_boolean_default(prop, false); + RNA_def_property_ui_text( + prop, "Compositor", "Preview the compositor output inside the viewport"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, NULL); } static void rna_def_space_view3d_overlay(BlenderRNA *brna) diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index dabb89bcd5e..adb959944b5 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -473,7 +473,7 @@ static int rna_UIList_list_id_length(PointerRNA *ptr) } static void uilist_draw_item(uiList *ui_list, - bContext *C, + const bContext *C, uiLayout *layout, PointerRNA *dataptr, PointerRNA *itemptr, @@ -507,7 +507,7 @@ static void uilist_draw_item(uiList *ui_list, RNA_parameter_list_free(&list); } -static void uilist_draw_filter(uiList *ui_list, bContext *C, uiLayout *layout) +static void uilist_draw_filter(uiList *ui_list, const bContext *C, uiLayout *layout) { extern FunctionRNA rna_UIList_draw_filter_func; @@ -527,7 +527,7 @@ static void uilist_draw_filter(uiList *ui_list, bContext *C, uiLayout *layout) } static void uilist_filter_items(uiList *ui_list, - bContext *C, + const bContext *C, PointerRNA *dataptr, const char *propname) { diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index 1b416e4b6e5..fc68e8421d7 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -1808,8 +1808,7 @@ void RNA_api_ui_layout(StructRNA *srna) func = RNA_def_function( srna, "template_colormanaged_view_settings", "uiTemplateColormanagedViewSettings"); - RNA_def_function_ui_description( - func, "Item. A widget to control color managed view settings settings."); + RNA_def_function_ui_description(func, "Item. A widget to control color managed view settings."); RNA_def_function_flag(func, FUNC_USE_CONTEXT); api_ui_item_rna_common(func); # if 0 diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index cfc72791123..438bac9b458 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -6338,6 +6338,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Sculpt Mode Tilt Support", "Support for pen tablet tilt events in Sculpt Mode"); + prop = RNA_def_property(srna, "use_realtime_compositor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_realtime_compositor", 1); + RNA_def_property_ui_text(prop, "Realtime Compositor", "Enable the new realtime compositor"); + prop = RNA_def_property(srna, "use_sculpt_texture_paint", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_sculpt_texture_paint", 1); RNA_def_property_ui_text(prop, "Sculpt Texture Paint", "Use texture painting in Sculpt Mode"); diff --git a/source/blender/modifiers/intern/MOD_armature.c b/source/blender/modifiers/intern/MOD_armature.c index 15ad361a262..43f650e025c 100644 --- a/source/blender/modifiers/intern/MOD_armature.c +++ b/source/blender/modifiers/intern/MOD_armature.c @@ -125,7 +125,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, amd->object, DEG_OB_COMP_TRANSFORM, "Armature Modifier"); } - DEG_add_modifier_to_transform_relation(ctx->node, "Armature Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Armature Modifier"); } static void deformVerts(ModifierData *md, @@ -211,8 +211,7 @@ static void deformMatrices(ModifierData *md, int verts_num) { ArmatureModifierData *amd = (ArmatureModifierData *)md; - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); BKE_armature_deform_coords_with_mesh(amd->object, ctx->object, diff --git a/source/blender/modifiers/intern/MOD_array.c b/source/blender/modifiers/intern/MOD_array.c index 569b0fd0fda..b29b34436ca 100644 --- a/source/blender/modifiers/intern/MOD_array.c +++ b/source/blender/modifiers/intern/MOD_array.c @@ -93,7 +93,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_dependency) { - DEG_add_modifier_to_transform_relation(ctx->node, "Array Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Array Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index aa64c1f83bc..685338cf351 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -117,7 +117,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_collection_geometry_relation(ctx->node, col, "Boolean Modifier"); } /* We need own transformation as well. */ - DEG_add_modifier_to_transform_relation(ctx->node, "Boolean Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Boolean Modifier"); } static Mesh *get_quick_mesh( diff --git a/source/blender/modifiers/intern/MOD_cast.c b/source/blender/modifiers/intern/MOD_cast.c index 9aaf7fead36..e17a612376d 100644 --- a/source/blender/modifiers/intern/MOD_cast.c +++ b/source/blender/modifiers/intern/MOD_cast.c @@ -87,7 +87,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte CastModifierData *cmd = (CastModifierData *)md; if (cmd->object != NULL) { DEG_add_object_relation(ctx->node, cmd->object, DEG_OB_COMP_TRANSFORM, "Cast Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "Cast Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Cast Modifier"); } } @@ -467,7 +467,7 @@ static void deformVerts(ModifierData *md, if (ctx->object->type == OB_MESH && cmd->defgrp_name[0] != '\0') { /* mesh_src is only needed for vgroups. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } if (cmd->type == MOD_CAST_TYPE_CUBOID) { @@ -493,15 +493,14 @@ static void deformVertsEM(ModifierData *md, Mesh *mesh_src = NULL; if (cmd->defgrp_name[0] != '\0') { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { BLI_assert(mesh->totvert == verts_num); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index cc0bd87d614..11bbe8dc83e 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -94,7 +94,7 @@ static void deformVerts(ModifierData *md, } if (mesh == NULL) { - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false); } else { /* Not possible to use get_mesh() in this case as we'll modify its vertices @@ -144,7 +144,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_forcefield_relations( ctx->node, ctx->object, clmd->sim_parms->effector_weights, true, 0, "Cloth Field"); } - DEG_add_modifier_to_transform_relation(ctx->node, "Cloth Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Cloth Modifier"); } static void requiredDataMask(Object *UNUSED(ob), diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 74cb4ac700a..42a8ba804ed 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -107,7 +107,7 @@ static void deformVerts(ModifierData *md, } if (mesh == NULL) { - mesh_src = MOD_deform_mesh_eval_get(ob, NULL, NULL, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ob, NULL, NULL, NULL, verts_num, false); } else { /* Not possible to use get_mesh() in this case as we'll modify its vertices @@ -236,7 +236,7 @@ static void deformVerts(ModifierData *md, static void updateDepsgraph(ModifierData *UNUSED(md), const ModifierUpdateDepsgraphContext *ctx) { - DEG_add_modifier_to_transform_relation(ctx->node, "Collision Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Collision Modifier"); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/modifiers/intern/MOD_correctivesmooth.c b/source/blender/modifiers/intern/MOD_correctivesmooth.c index 2beb1be6749..4df0479372f 100644 --- a/source/blender/modifiers/intern/MOD_correctivesmooth.c +++ b/source/blender/modifiers/intern/MOD_correctivesmooth.c @@ -729,8 +729,7 @@ static void deformVerts(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); correctivesmooth_modifier_do( md, ctx->depsgraph, ctx->object, mesh_src, vertexCos, (uint)verts_num, NULL); @@ -747,10 +746,9 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_curve.c b/source/blender/modifiers/intern/MOD_curve.c index 48a59f4d949..af639915bd8 100644 --- a/source/blender/modifiers/intern/MOD_curve.c +++ b/source/blender/modifiers/intern/MOD_curve.c @@ -97,7 +97,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_special_eval_flag(ctx->node, &cmd->object->id, DAG_EVAL_NEED_CURVE_PATH); } - DEG_add_modifier_to_transform_relation(ctx->node, "Curve Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Curve Modifier"); } static void deformVerts(ModifierData *md, @@ -111,7 +111,7 @@ static void deformVerts(ModifierData *md, if (ctx->object->type == OB_MESH && cmd->name[0] != '\0') { /* mesh_src is only needed for vgroups. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } struct MDeformVert *dvert = NULL; diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index e9f1cf47e38..7cd6b829d37 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -129,7 +129,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte if (dtmd->flags & MOD_DATATRANSFER_OBSRC_TRANSFORM) { DEG_add_object_relation( ctx->node, dtmd->ob_source, DEG_OB_COMP_TRANSFORM, "DataTransfer Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "DataTransfer Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "DataTransfer Modifier"); } } } diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 3df4fbcbea8..55d9d148d10 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -201,11 +201,12 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * updateFaceCount(ctx, dmd, bm->totface); - result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); /* make sure we never alloc'd these */ BLI_assert(bm->vtoolflagpool == NULL && bm->etoolflagpool == NULL && bm->ftoolflagpool == NULL); BLI_assert(bm->vtable == NULL && bm->etable == NULL && bm->ftable == NULL); + result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); #ifdef USE_TIMEIT diff --git a/source/blender/modifiers/intern/MOD_displace.c b/source/blender/modifiers/intern/MOD_displace.c index 5289fc42e21..367809953b6 100644 --- a/source/blender/modifiers/intern/MOD_displace.c +++ b/source/blender/modifiers/intern/MOD_displace.c @@ -142,7 +142,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "Displace Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Displace Modifier"); } } @@ -372,8 +372,7 @@ static void deformVerts(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); displaceModifier_do((DisplaceModifierData *)md, ctx, mesh_src, vertexCos, verts_num); @@ -389,10 +388,9 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_explode.c b/source/blender/modifiers/intern/MOD_explode.c index ff0616fd288..e243c32173d 100644 --- a/source/blender/modifiers/intern/MOD_explode.c +++ b/source/blender/modifiers/intern/MOD_explode.c @@ -742,7 +742,7 @@ static Mesh *cutEdges(ExplodeModifierData *emd, Mesh *mesh) /* override original facepa (original pointer is saved in caller function) */ - /* TODO(campbell): `(totfsplit * 2)` over allocation is used since the quads are + /* TODO(@campbellbarton): `(totfsplit * 2)` over allocation is used since the quads are * later interpreted as tri's, for this to work right I think we probably * have to stop using tessface. */ diff --git a/source/blender/modifiers/intern/MOD_hook.c b/source/blender/modifiers/intern/MOD_hook.c index 3c4e6b0d90f..979a08483e1 100644 --- a/source/blender/modifiers/intern/MOD_hook.c +++ b/source/blender/modifiers/intern/MOD_hook.c @@ -122,7 +122,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, hmd->object, DEG_OB_COMP_TRANSFORM, "Hook Modifier"); } /* We need own transformation as well. */ - DEG_add_modifier_to_transform_relation(ctx->node, "Hook Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Hook Modifier"); } struct HookData_cb { @@ -430,8 +430,7 @@ static void deformVerts(struct ModifierData *md, int verts_num) { HookModifierData *hmd = (HookModifierData *)md; - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); deformVerts_do(hmd, ctx, ctx->object, mesh_src, NULL, vertexCos, verts_num); diff --git a/source/blender/modifiers/intern/MOD_laplaciandeform.c b/source/blender/modifiers/intern/MOD_laplaciandeform.c index e29098eb218..6333eb699b3 100644 --- a/source/blender/modifiers/intern/MOD_laplaciandeform.c +++ b/source/blender/modifiers/intern/MOD_laplaciandeform.c @@ -764,8 +764,7 @@ static void deformVerts(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); LaplacianDeformModifier_do( (LaplacianDeformModifierData *)md, ctx->object, mesh_src, vertexCos, verts_num); @@ -782,10 +781,9 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_laplaciansmooth.c b/source/blender/modifiers/intern/MOD_laplaciansmooth.c index 2cce0c14e4c..c42f7b33919 100644 --- a/source/blender/modifiers/intern/MOD_laplaciansmooth.c +++ b/source/blender/modifiers/intern/MOD_laplaciansmooth.c @@ -535,7 +535,7 @@ static void deformVerts(ModifierData *md, return; } - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); laplaciansmoothModifier_do( (LaplacianSmoothModifierData *)md, ctx->object, mesh_src, vertexCos, verts_num); @@ -558,9 +558,9 @@ static void deformVertsEM(ModifierData *md, return; } - mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_lattice.c b/source/blender/modifiers/intern/MOD_lattice.c index 0e1994eed36..81b60b660c6 100644 --- a/source/blender/modifiers/intern/MOD_lattice.c +++ b/source/blender/modifiers/intern/MOD_lattice.c @@ -87,7 +87,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_GEOMETRY, "Lattice Modifier"); DEG_add_object_relation(ctx->node, lmd->object, DEG_OB_COMP_TRANSFORM, "Lattice Modifier"); } - DEG_add_modifier_to_transform_relation(ctx->node, "Lattice Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Lattice Modifier"); } static void deformVerts(ModifierData *md, @@ -98,7 +98,7 @@ static void deformVerts(ModifierData *md, { LatticeModifierData *lmd = (LatticeModifierData *)md; struct Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + ctx->object, NULL, mesh, NULL, verts_num, false); MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */ diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index fac3ea36537..e48a949baf4 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -86,7 +86,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte /* TODO(sergey): Is it a proper relation here? */ DEG_add_object_relation(ctx->node, mmd->ob_arm, DEG_OB_COMP_TRANSFORM, "Mask Modifier"); arm->flag |= ARM_HAS_VIZ_DEPS; - DEG_add_modifier_to_transform_relation(ctx->node, "Mask Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Mask Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc index 9ac410eb3de..0471beadcc1 100644 --- a/source/blender/modifiers/intern/MOD_mesh_to_volume.cc +++ b/source/blender/modifiers/intern/MOD_mesh_to_volume.cc @@ -60,7 +60,7 @@ static void initData(ModifierData *md) static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { MeshToVolumeModifierData *mvmd = reinterpret_cast<MeshToVolumeModifierData *>(md); - DEG_add_modifier_to_transform_relation(ctx->node, "Mesh to Volume Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Mesh to Volume Modifier"); if (mvmd->object) { DEG_add_object_relation( ctx->node, mvmd->object, DEG_OB_COMP_GEOMETRY, "Mesh to Volume Modifier"); diff --git a/source/blender/modifiers/intern/MOD_meshcache.c b/source/blender/modifiers/intern/MOD_meshcache.c index 8dfdd07ace9..3e81f987da3 100644 --- a/source/blender/modifiers/intern/MOD_meshcache.c +++ b/source/blender/modifiers/intern/MOD_meshcache.c @@ -297,7 +297,7 @@ static void deformVerts(ModifierData *md, if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') { /* `mesh_src` is only needed for vertex groups. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } meshcache_do(mcmd, scene, ctx->object, mesh_src, vertexCos, verts_num); @@ -320,8 +320,7 @@ static void deformVertsEM(ModifierData *md, if (ctx->object->type == OB_MESH && mcmd->defgrp_name[0] != '\0') { /* `mesh_src` is only needed for vertex groups. */ - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index 334f5d75279..d1df86b1010 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -160,7 +160,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_object_relation(ctx->node, mmd->object, DEG_OB_COMP_GEOMETRY, "Mesh Deform Modifier"); } /* We need own transformation as well. */ - DEG_add_modifier_to_transform_relation(ctx->node, "Mesh Deform Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Mesh Deform Modifier"); } static float meshdeform_dynamic_bind(MeshDeformModifierData *mmd, float (*dco)[3], float vec[3]) @@ -444,8 +444,7 @@ static void deformVerts(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); MOD_previous_vcos_store(md, vertexCos); /* if next modifier needs original vertices */ @@ -463,10 +462,9 @@ static void deformVertsEM(ModifierData *md, float (*vertexCos)[3], int verts_num) { - Mesh *mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + Mesh *mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_mirror.c b/source/blender/modifiers/intern/MOD_mirror.c index 5f095a72dca..f1a36c04453 100644 --- a/source/blender/modifiers/intern/MOD_mirror.c +++ b/source/blender/modifiers/intern/MOD_mirror.c @@ -62,7 +62,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte MirrorModifierData *mmd = (MirrorModifierData *)md; if (mmd->mirror_ob != NULL) { DEG_add_object_relation(ctx->node, mmd->mirror_ob, DEG_OB_COMP_TRANSFORM, "Mirror Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "Mirror Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Mirror Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 223e4b757b7..9c95561904a 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -40,7 +40,7 @@ #include "BKE_geometry_fields.hh" #include "BKE_geometry_set_instances.hh" #include "BKE_global.h" -#include "BKE_idprop.h" +#include "BKE_idprop.hh" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -307,7 +307,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (needs_own_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "Nodes Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Nodes Modifier"); } } @@ -416,15 +416,16 @@ static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int soc return field_interface.inputs[socket_index] != InputSocketFieldType::None; } -static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) +static std::unique_ptr<IDProperty, blender::bke::idprop::IDPropertyDeleter> +id_property_create_from_socket(const bNodeSocket &socket) { + using namespace blender; switch (socket.type) { case SOCK_FLOAT: { - bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.f = value->value; - IDProperty *property = IDP_New(IDP_FLOAT, &idprop, socket.identifier); - IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + const bNodeSocketValueFloat *value = static_cast<const bNodeSocketValueFloat *>( + socket.default_value); + auto property = bke::idprop::create(socket.identifier, value->value); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->min = ui_data->soft_min = (double)value->min; ui_data->max = ui_data->soft_max = (double)value->max; @@ -432,11 +433,10 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) return property; } case SOCK_INT: { - bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->value; - IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier); - IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property); + const bNodeSocketValueInt *value = static_cast<const bNodeSocketValueInt *>( + socket.default_value); + auto property = bke::idprop::create(socket.identifier, value->value); + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->min = ui_data->soft_min = value->min; ui_data->max = ui_data->soft_max = value->max; @@ -444,13 +444,11 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) return property; } case SOCK_VECTOR: { - bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.array.len = 3; - idprop.array.type = IDP_FLOAT; - IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier); - copy_v3_v3((float *)IDP_Array(property), value->value); - IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + const bNodeSocketValueVector *value = static_cast<const bNodeSocketValueVector *>( + socket.default_value); + auto property = bke::idprop::create( + socket.identifier, Span<float>{value->value[0], value->value[1], value->value[2]}); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = value->subtype; ui_data->min = ui_data->soft_min = (double)value->min; ui_data->max = ui_data->soft_max = (double)value->max; @@ -462,13 +460,12 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) return property; } case SOCK_RGBA: { - bNodeSocketValueRGBA *value = (bNodeSocketValueRGBA *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.array.len = 4; - idprop.array.type = IDP_FLOAT; - IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier); - copy_v4_v4((float *)IDP_Array(property), value->value); - IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + const bNodeSocketValueRGBA *value = static_cast<const bNodeSocketValueRGBA *>( + socket.default_value); + auto property = bke::idprop::create( + socket.identifier, + Span<float>{value->value[0], value->value[1], value->value[2], value->value[3]}); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property.get()); ui_data->base.rna_subtype = PROP_COLOR; ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__); ui_data->default_array_len = 4; @@ -482,53 +479,48 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) return property; } case SOCK_BOOLEAN: { - bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->value != 0; - IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier); - IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property); + const bNodeSocketValueBoolean *value = static_cast<const bNodeSocketValueBoolean *>( + socket.default_value); + auto property = bke::idprop::create(socket.identifier, int(value->value)); + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property.get()); ui_data->min = ui_data->soft_min = 0; ui_data->max = ui_data->soft_max = 1; ui_data->default_value = value->value != 0; return property; } case SOCK_STRING: { - bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value; - IDProperty *property = IDP_NewString( - value->value, socket.identifier, BLI_strnlen(value->value, sizeof(value->value)) + 1); - IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(property); + const bNodeSocketValueString *value = static_cast<const bNodeSocketValueString *>( + socket.default_value); + auto property = bke::idprop::create(socket.identifier, value->value); + IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure( + property.get()); ui_data->default_value = BLI_strdup(value->value); return property; } case SOCK_OBJECT: { - bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueObject *value = static_cast<const bNodeSocketValueObject *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } case SOCK_COLLECTION: { - bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueCollection *value = static_cast<const bNodeSocketValueCollection *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } case SOCK_TEXTURE: { - bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueTexture *value = static_cast<const bNodeSocketValueTexture *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } case SOCK_IMAGE: { - bNodeSocketValueImage *value = (bNodeSocketValueImage *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueImage *value = static_cast<const bNodeSocketValueImage *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } case SOCK_MATERIAL: { - bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, socket.identifier); + const bNodeSocketValueMaterial *value = static_cast<const bNodeSocketValueMaterial *>( + socket.default_value); + return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value)); } } return nullptr; @@ -658,7 +650,7 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) int socket_index; LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) { - IDProperty *new_prop = id_property_create_from_socket(*socket); + IDProperty *new_prop = id_property_create_from_socket(*socket).release(); if (new_prop == nullptr) { /* Out of the set of supported input sockets, only * geometry sockets aren't added to the modifier. */ diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index e43d2b4ad85..5cf4e21ea68 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -1863,6 +1863,7 @@ bool NodeParamsProvider::lazy_require_input(StringRef identifier) void NodeParamsProvider::set_input_unused(StringRef identifier) { + BLI_assert(node_supports_laziness(this->dnode)); const DInputSocket socket = this->dnode.input_by_identifier(identifier); BLI_assert(socket); diff --git a/source/blender/modifiers/intern/MOD_normal_edit.c b/source/blender/modifiers/intern/MOD_normal_edit.c index 09bc9546325..94b48f65a66 100644 --- a/source/blender/modifiers/intern/MOD_normal_edit.c +++ b/source/blender/modifiers/intern/MOD_normal_edit.c @@ -670,7 +670,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte NormalEditModifierData *enmd = (NormalEditModifierData *)md; if (enmd->target) { DEG_add_object_relation(ctx->node, enmd->target, DEG_OB_COMP_TRANSFORM, "NormalEdit Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "NormalEdit Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "NormalEdit Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_particlesystem.cc b/source/blender/modifiers/intern/MOD_particlesystem.cc index 7f7465947f9..0c04c6fc062 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.cc +++ b/source/blender/modifiers/intern/MOD_particlesystem.cc @@ -119,8 +119,7 @@ static void deformVerts(ModifierData *md, } if (mesh_src == nullptr) { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, nullptr, nullptr, vertexCos, verts_num, false, true); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, nullptr, nullptr, vertexCos, verts_num, true); if (mesh_src == nullptr) { return; } diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 9588b9acd3b..6095be48f8f 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -52,13 +52,18 @@ static void initData(ModifierData *md) #include "BLI_strict_flags.h" -/* used for gathering edge connectivity */ +/** Used for gathering edge connectivity. */ typedef struct ScrewVertConnect { - float dist; /* distance from the center axis */ - float co[3]; /* location relative to the transformed axis */ - float no[3]; /* calc normal of the vertex */ - uint v[2]; /* 2 verts on either side of this one */ - MEdge *e[2]; /* edges on either side, a bit of a waste since each edge ref's 2 edges */ + /** Distance from the center axis. */ + float dist_sq; + /** Location relative to the transformed axis. */ + float co[3]; + /** Calc normal of the vertex. */ + float no[3]; + /** 2 verts on either side of this one. */ + uint v[2]; + /** Edges on either side, a bit of a waste since each edge ref's 2 edges. */ + MEdge *e[2]; char flag; } ScrewVertConnect; @@ -270,18 +275,18 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * axis_vec[ltmd->axis] = 1.0f; if (ob_axis != NULL) { - /* calc the matrix relative to the axis object */ + /* Calculate the matrix relative to the axis object. */ invert_m4_m4(mtx_tmp_a, ctx->object->obmat); copy_m4_m4(mtx_tx_inv, ob_axis->obmat); mul_m4_m4m4(mtx_tx, mtx_tmp_a, mtx_tx_inv); - /* calc the axis vec */ + /* Calculate the axis vector. */ mul_mat3_m4_v3(mtx_tx, axis_vec); /* only rotation component */ normalize_v3(axis_vec); /* screw */ if (ltmd->flag & MOD_SCREW_OBJECT_OFFSET) { - /* find the offset along this axis relative to this objects matrix */ + /* Find the offset along this axis relative to this objects matrix. */ float totlen = len_v3(mtx_tx[3]); if (totlen != 0.0f) { @@ -330,7 +335,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * else { axis_char = (char)(axis_char + ltmd->axis); /* 'X' + axis */ - /* useful to be able to use the axis vec in some cases still */ + /* Useful to be able to use the axis vector in some cases still. */ zero_v3(axis_vec); axis_vec[ltmd->axis] = 1.0f; } @@ -441,7 +446,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * med_new->crease = med_orig->crease; med_new->flag = med_orig->flag & ~ME_LOOSEEDGE; - /* Tag mvert as not loose. */ + /* Tag #MVert as not loose. */ BLI_BITMAP_ENABLE(vert_tag, med_orig->v1); BLI_BITMAP_ENABLE(vert_tag, med_orig->v2); } @@ -481,8 +486,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * if (ltmd->flag & MOD_SCREW_NORMAL_CALC) { - /* - * Normal Calculation (for face flipping) + /* Normal Calculation (for face flipping) * Sort edge verts for correct face flipping * NOT REALLY NEEDED but face flipping is nice. */ @@ -490,19 +494,19 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * * * Since we are only ordering the edges here it can avoid mallocing the * extra space by abusing the vert array before its filled with new verts. - * The new array for vert_connect must be at least sizeof(ScrewVertConnect) * totvert - * and the size of our resulting meshes array is sizeof(MVert) * totvert * 3 - * so its safe to use the second 2 thirds of MVert the array for vert_connect, - * just make sure ScrewVertConnect struct is no more than twice as big as MVert, + * The new array for vert_connect must be at least `sizeof(ScrewVertConnect) * totvert` + * and the size of our resulting meshes array is `sizeof(MVert) * totvert * 3` + * so its safe to use the second 2 thirds of #MVert the array for vert_connect, + * just make sure #ScrewVertConnect struct is no more than twice as big as #MVert, * at the moment there is no chance of that being a problem, - * unless MVert becomes half its current size. + * unless #MVert becomes half its current size. * * once the edges are ordered, vert_connect is not needed and it can be used for verts * - * This makes the modifier faster with one less alloc. + * This makes the modifier faster with one less allocate. */ - vert_connect = MEM_malloc_arrayN(totvert, sizeof(ScrewVertConnect), "ScrewVertConnect"); + vert_connect = MEM_malloc_arrayN(totvert, sizeof(ScrewVertConnect), __func__); /* skip the first slice of verts. */ // vert_connect = (ScrewVertConnect *) &medge_new[totvert]; vc = vert_connect; @@ -512,7 +516,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * if (!totedge) { for (i = 0; i < totvert; i++, mv_orig++, mv_new++) { copy_v3_v3(mv_new->co, mv_orig->co); - normalize_v3_v3(vc->no, mv_new->co); /* no edges- this is really a dummy normal */ + /* No edges: this is really a dummy normal. */ + normalize_v3_v3(vc->no, mv_new->co); } } else { @@ -533,11 +538,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * vc->v[0] = vc->v[1] = SV_UNUSED; mul_m4_v3(mtx_tx, vc->co); - /* length in 2d, don't sqrt because this is only for comparison */ - vc->dist = vc->co[other_axis_1] * vc->co[other_axis_1] + - vc->co[other_axis_2] * vc->co[other_axis_2]; + /* Length in 2D, don't `sqrt` because this is only for comparison. */ + vc->dist_sq = vc->co[other_axis_1] * vc->co[other_axis_1] + + vc->co[other_axis_2] * vc->co[other_axis_2]; - // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist); + // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist_sq); } } else { @@ -550,11 +555,11 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * vc->e[0] = vc->e[1] = NULL; vc->v[0] = vc->v[1] = SV_UNUSED; - /* length in 2d, don't sqrt because this is only for comparison */ - vc->dist = vc->co[other_axis_1] * vc->co[other_axis_1] + - vc->co[other_axis_2] * vc->co[other_axis_2]; + /* Length in 2D, don't sqrt because this is only for comparison. */ + vc->dist_sq = vc->co[other_axis_1] * vc->co[other_axis_1] + + vc->co[other_axis_2] * vc->co[other_axis_2]; - // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist); + // printf("location %f %f %f -- %f\n", vc->co[0], vc->co[1], vc->co[2], vc->dist_sq); } } @@ -622,9 +627,9 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } lt_iter.v_poin->flag = 1; vc_tot_linked++; - // printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist); - if (fl <= lt_iter.v_poin->dist) { - fl = lt_iter.v_poin->dist; + // printf("Testing 2 floats %f : %f\n", fl, lt_iter.v_poin->dist_sq); + if (fl <= lt_iter.v_poin->dist_sq) { + fl = lt_iter.v_poin->dist_sq; v_best = lt_iter.v; // printf("\t\t\tVERT BEST: %i\n", v_best); } @@ -1148,7 +1153,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte ScrewModifierData *ltmd = (ScrewModifierData *)md; if (ltmd->ob_axis != NULL) { DEG_add_object_relation(ctx->node, ltmd->ob_axis, DEG_OB_COMP_TRANSFORM, "Screw Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "Screw Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Screw Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_shrinkwrap.c b/source/blender/modifiers/intern/MOD_shrinkwrap.c index be12dc6639b..4a927d92956 100644 --- a/source/blender/modifiers/intern/MOD_shrinkwrap.c +++ b/source/blender/modifiers/intern/MOD_shrinkwrap.c @@ -108,7 +108,7 @@ static void deformVerts(ModifierData *md, (swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) { /* mesh_src is needed for vgroups, but also used as ShrinkwrapCalcData.vert when projecting. * Avoid time-consuming mesh conversion for curves when not projecting. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } struct MDeformVert *dvert = NULL; @@ -135,11 +135,10 @@ static void deformVertsEM(ModifierData *md, Mesh *mesh_src = NULL; if ((swmd->vgroup_name[0] != '\0') || (swmd->shrinkType == MOD_SHRINKWRAP_PROJECT)) { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } @@ -186,7 +185,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte DEG_add_special_eval_flag(ctx->node, &smd->auxTarget->id, DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY); } } - DEG_add_modifier_to_transform_relation(ctx->node, "Shrinkwrap Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Shrinkwrap Modifier"); } static bool dependsOnNormals(ModifierData *md) diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index 9f1d0cd36c4..1fc4f11bc66 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -438,7 +438,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte if (smd->origin != NULL) { DEG_add_object_relation( ctx->node, smd->origin, DEG_OB_COMP_TRANSFORM, "SimpleDeform Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "SimpleDeform Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "SimpleDeform Modifier"); } } @@ -453,7 +453,7 @@ static void deformVerts(ModifierData *md, if (ctx->object->type == OB_MESH && sdmd->vgroup_name[0] != '\0') { /* mesh_src is only needed for vgroups. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } SimpleDeformModifier_do(sdmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); @@ -475,11 +475,10 @@ static void deformVertsEM(ModifierData *md, if (ctx->object->type == OB_MESH && sdmd->vgroup_name[0] != '\0') { /* mesh_src is only needed for vgroups. */ - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_smooth.c b/source/blender/modifiers/intern/MOD_smooth.c index c868c47cb90..6dd3d491283 100644 --- a/source/blender/modifiers/intern/MOD_smooth.c +++ b/source/blender/modifiers/intern/MOD_smooth.c @@ -190,7 +190,7 @@ static void deformVerts(ModifierData *md, Mesh *mesh_src = NULL; /* mesh_src is needed for vgroups, and taking edges into account. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); smoothModifier_do(smd, ctx->object, mesh_src, vertexCos, verts_num); @@ -210,9 +210,9 @@ static void deformVertsEM(ModifierData *md, Mesh *mesh_src = NULL; /* mesh_src is needed for vgroups, and taking edges into account. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); - /* TODO(campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ BKE_mesh_wrapper_ensure_mdata(mesh_src); smoothModifier_do(smd, ctx->object, mesh_src, vertexCos, verts_num); diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index a49f2609641..ecff6d80893 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -66,7 +66,7 @@ static void updateDepsgraph(ModifierData *UNUSED(md), const ModifierUpdateDepsgr ctx->node, ctx->object, ctx->object->soft->effector_weights, true, 0, "Softbody Field"); } /* We need own transformation as well. */ - DEG_add_modifier_to_transform_relation(ctx->node, "SoftBody Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "SoftBody Modifier"); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 80af23054e4..47277577d3e 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -53,7 +53,13 @@ BLI_INLINE bool edgeref_is_init(const EdgeFaceRef *edge_ref) * \param poly_nors: Precalculated face normals. * \param r_vert_nors: Return vert normals. */ -static void mesh_calc_hq_normal(Mesh *mesh, const float (*poly_nors)[3], float (*r_vert_nors)[3]) +static void mesh_calc_hq_normal(Mesh *mesh, + const float (*poly_nors)[3], + float (*r_vert_nors)[3], +#ifdef USE_NONMANIFOLD_WORKAROUND + BLI_bitmap *edge_tmp_tag +#endif +) { int i, verts_num, edges_num, polys_num; MPoly *mpoly, *mp; @@ -103,7 +109,7 @@ static void mesh_calc_hq_normal(Mesh *mesh, const float (*poly_nors)[3], float ( /* 3+ faces using an edge, we can't handle this usefully */ edge_ref->p1 = edge_ref->p2 = -1; #ifdef USE_NONMANIFOLD_WORKAROUND - medge[ml->e].flag |= ME_EDGE_TMP_TAG; + BLI_BITMAP_ENABLE(edge_tmp_tag, ml->e); #endif } /* --- done --- */ @@ -319,9 +325,20 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex BLI_assert(newEdges == 0); } +#ifdef USE_NONMANIFOLD_WORKAROUND + BLI_bitmap *edge_tmp_tag = BLI_BITMAP_NEW(mesh->totedge, __func__); +#endif + if (smd->flag & MOD_SOLIDIFY_NORMAL_CALC) { vert_nors = MEM_calloc_arrayN(verts_num, sizeof(float[3]), "mod_solid_vno_hq"); - mesh_calc_hq_normal(mesh, poly_nors, vert_nors); + mesh_calc_hq_normal(mesh, + poly_nors, + vert_nors +#ifdef USE_NONMANIFOLD_WORKAROUND + , + edge_tmp_tag +#endif + ); } result = BKE_mesh_new_nomain_from_template(mesh, @@ -740,8 +757,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex #ifdef USE_NONMANIFOLD_WORKAROUND /* skip 3+ face user edges */ if ((check_non_manifold == false) || - LIKELY(((orig_medge[ml[i_curr].e].flag & ME_EDGE_TMP_TAG) == 0) && - ((orig_medge[ml[i_next].e].flag & ME_EDGE_TMP_TAG) == 0))) { + LIKELY(!BLI_BITMAP_TEST(edge_tmp_tag, ml[i_curr].e) && + !BLI_BITMAP_TEST(edge_tmp_tag, ml[i_next].e))) { vert_angles[vidx] += shell_v3v3_normalized_to_dist(vert_nors[vidx], poly_nors[i]) * angle; } @@ -949,6 +966,10 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex MEM_freeN(vert_angles); } +#ifdef USE_NONMANIFOLD_WORKAROUND + MEM_SAFE_FREE(edge_tmp_tag); +#endif + if (vert_nors) { MEM_freeN(vert_nors); } @@ -999,9 +1020,9 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex if (do_rim) { uint i; - /* NOTE(campbell): Unfortunately re-calculate the normals for the new edge faces is necessary. - * This could be done in many ways, but probably the quickest way - * is to calculate the average normals for side faces only. + /* NOTE(@campbellbarton): Unfortunately re-calculate the normals for the new edge + * faces is necessary. This could be done in many ways, but probably the quickest + * way is to calculate the average normals for side faces only. * Then blend them with the normals of the edge verts. * * At the moment its easiest to allocate an entire array for every vertex, diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 8cfe3b35949..3e5a577a806 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -114,7 +114,7 @@ static void deformVerts(ModifierData *md, surmd->mesh = (Mesh *)BKE_id_copy_ex(NULL, (ID *)mesh, NULL, LIB_ID_COPY_LOCALIZE); } else { - surmd->mesh = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false, false); + surmd->mesh = MOD_deform_mesh_eval_get(ctx->object, NULL, NULL, NULL, verts_num, false); } if (!ctx->object->pd) { diff --git a/source/blender/modifiers/intern/MOD_surfacedeform.c b/source/blender/modifiers/intern/MOD_surfacedeform.c index ad19ecf5720..3c0842a6e93 100644 --- a/source/blender/modifiers/intern/MOD_surfacedeform.c +++ b/source/blender/modifiers/intern/MOD_surfacedeform.c @@ -215,8 +215,7 @@ static void freeData(ModifierData *md) MEM_SAFE_FREE(smd->verts[i].binds[j].vert_inds); MEM_SAFE_FREE(smd->verts[i].binds[j].vert_weights); } - - MEM_SAFE_FREE(smd->verts[i].binds); + MEM_freeN(smd->verts[i].binds); } } @@ -1578,7 +1577,7 @@ static void deformVerts(ModifierData *md, if (smd->defgrp_name[0] != '\0') { /* Only need to use mesh_src when a vgroup is used. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } surfacedeformModifier_do(md, ctx, vertexCos, verts_num, ctx->object, mesh_src); @@ -1600,7 +1599,7 @@ static void deformVertsEM(ModifierData *md, if (smd->defgrp_name[0] != '\0') { /* Only need to use mesh_src when a vgroup is used. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false); } surfacedeformModifier_do(md, ctx, vertexCos, verts_num, ctx->object, mesh_src); diff --git a/source/blender/modifiers/intern/MOD_util.c b/source/blender/modifiers/intern/MOD_util.c index 575182a846b..fc17ddffa87 100644 --- a/source/blender/modifiers/intern/MOD_util.c +++ b/source/blender/modifiers/intern/MOD_util.c @@ -169,7 +169,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, Mesh *mesh, const float (*vertexCos)[3], const int verts_num, - const bool use_normals, const bool use_orco) { if (mesh != NULL) { @@ -217,14 +216,6 @@ Mesh *MOD_deform_mesh_eval_get(Object *ob, } } - /* TODO: Remove this "use_normals" argument, since the caller should retrieve normals afterwards - * if necessary. */ - if (use_normals) { - if (LIKELY(mesh)) { - BKE_mesh_vertex_normals_ensure(mesh); - } - } - if (mesh && mesh->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA) { BLI_assert(mesh->totvert == verts_num); } diff --git a/source/blender/modifiers/intern/MOD_util.h b/source/blender/modifiers/intern/MOD_util.h index b3b75898557..b675c11b370 100644 --- a/source/blender/modifiers/intern/MOD_util.h +++ b/source/blender/modifiers/intern/MOD_util.h @@ -42,7 +42,6 @@ struct Mesh *MOD_deform_mesh_eval_get(struct Object *ob, struct Mesh *mesh, const float (*vertexCos)[3], int verts_num, - bool use_normals, bool use_orco); void MOD_get_vgroup(struct Object *ob, diff --git a/source/blender/modifiers/intern/MOD_uvproject.c b/source/blender/modifiers/intern/MOD_uvproject.c index 0474d3e47e6..a318a82fe64 100644 --- a/source/blender/modifiers/intern/MOD_uvproject.c +++ b/source/blender/modifiers/intern/MOD_uvproject.c @@ -80,7 +80,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } } if (do_add_own_transform) { - DEG_add_modifier_to_transform_relation(ctx->node, "UV Project Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "UV Project Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_uvwarp.c b/source/blender/modifiers/intern/MOD_uvwarp.c index c33b25c38e3..4178f1dd33e 100644 --- a/source/blender/modifiers/intern/MOD_uvwarp.c +++ b/source/blender/modifiers/intern/MOD_uvwarp.c @@ -242,7 +242,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte MOD_depsgraph_update_object_bone_relation( ctx->node, umd->object_dst, umd->bone_dst, "UVWarp Modifier"); - DEG_add_modifier_to_transform_relation(ctx->node, "UVWarp Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "UVWarp Modifier"); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) diff --git a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc index 3292f73137a..215436e4a8d 100644 --- a/source/blender/modifiers/intern/MOD_volume_to_mesh.cc +++ b/source/blender/modifiers/intern/MOD_volume_to_mesh.cc @@ -62,7 +62,7 @@ static void initData(ModifierData *md) static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphContext *ctx) { VolumeToMeshModifierData *vmmd = reinterpret_cast<VolumeToMeshModifierData *>(md); - DEG_add_modifier_to_transform_relation(ctx->node, "Volume to Mesh Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Volume to Mesh Modifier"); if (vmmd->object) { DEG_add_object_relation( ctx->node, vmmd->object, DEG_OB_COMP_GEOMETRY, "Volume to Mesh Modifier"); diff --git a/source/blender/modifiers/intern/MOD_warp.c b/source/blender/modifiers/intern/MOD_warp.c index afdc230a877..0968d0646a5 100644 --- a/source/blender/modifiers/intern/MOD_warp.c +++ b/source/blender/modifiers/intern/MOD_warp.c @@ -171,7 +171,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "Warp Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Warp Modifier"); } } @@ -348,7 +348,7 @@ static void deformVerts(ModifierData *md, if (wmd->defgrp_name[0] != '\0' || wmd->texture != NULL) { /* mesh_src is only needed for vgroups and textures. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } warpModifier_do(wmd, ctx, mesh_src, vertexCos, verts_num); @@ -370,10 +370,10 @@ static void deformVertsEM(ModifierData *md, if (wmd->defgrp_name[0] != '\0' || wmd->texture != NULL) { /* mesh_src is only needed for vgroups and textures. */ - mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, em, mesh, NULL, verts_num, false); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_wave.c b/source/blender/modifiers/intern/MOD_wave.c index ba7fb3fa1ba..9647f47c6e0 100644 --- a/source/blender/modifiers/intern/MOD_wave.c +++ b/source/blender/modifiers/intern/MOD_wave.c @@ -98,7 +98,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "Wave Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "Wave Modifier"); } } @@ -302,11 +302,10 @@ static void deformVerts(ModifierData *md, Mesh *mesh_src = NULL; if (wmd->flag & MOD_WAVE_NORM) { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, NULL, mesh, vertexCos, verts_num, true, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, vertexCos, verts_num, false); } else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') { - mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, NULL, mesh, NULL, verts_num, false); } waveModifier_do(wmd, ctx, ctx->object, mesh_src, vertexCos, verts_num); @@ -327,19 +326,13 @@ static void deformVertsEM(ModifierData *md, Mesh *mesh_src = NULL; if (wmd->flag & MOD_WAVE_NORM) { - /* NOTE(@campbellbarton): don't request normals here because `use_normals == false` - * because #BKE_mesh_wrapper_ensure_mdata has not run yet. - * While this could be supported the argument is documented to be removed, - * so pass false here and let the normals be created when requested. */ - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, vertexCos, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, vertexCos, verts_num, false); } else if (wmd->texture != NULL || wmd->defgrp_name[0] != '\0') { - mesh_src = MOD_deform_mesh_eval_get( - ctx->object, editData, mesh, NULL, verts_num, false, false); + mesh_src = MOD_deform_mesh_eval_get(ctx->object, editData, mesh, NULL, verts_num, false); } - /* TODO(Campbell): use edit-mode data only (remove this line). */ + /* TODO(@campbellbarton): use edit-mode data only (remove this line). */ if (mesh_src != NULL) { BKE_mesh_wrapper_ensure_mdata(mesh_src); } diff --git a/source/blender/modifiers/intern/MOD_weightvgedit.c b/source/blender/modifiers/intern/MOD_weightvgedit.c index 22f326d326e..8ccf140e665 100644 --- a/source/blender/modifiers/intern/MOD_weightvgedit.c +++ b/source/blender/modifiers/intern/MOD_weightvgedit.c @@ -139,7 +139,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGEdit Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "WeightVGEdit Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgmix.c b/source/blender/modifiers/intern/MOD_weightvgmix.c index 49088d42a5e..701e30fbf57 100644 --- a/source/blender/modifiers/intern/MOD_weightvgmix.c +++ b/source/blender/modifiers/intern/MOD_weightvgmix.c @@ -187,7 +187,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGMix Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "WeightVGMix Modifier"); } } diff --git a/source/blender/modifiers/intern/MOD_weightvgproximity.c b/source/blender/modifiers/intern/MOD_weightvgproximity.c index b68d36366fd..70838bc5c4f 100644 --- a/source/blender/modifiers/intern/MOD_weightvgproximity.c +++ b/source/blender/modifiers/intern/MOD_weightvgproximity.c @@ -401,7 +401,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte } if (need_transform_relation) { - DEG_add_modifier_to_transform_relation(ctx->node, "WeightVGProximity Modifier"); + DEG_add_depends_on_transform_relation(ctx->node, "WeightVGProximity Modifier"); } } diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index 4e78f6c1142..d8b8c354230 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -88,6 +88,14 @@ class SocketDeclaration { InputSocketFieldType input_field_type_ = InputSocketFieldType::None; OutputFieldDependency output_field_dependency_; + /** The priority of the input for determining the domain of the node. See + * realtime_compositor::InputDescriptor for more information. */ + int compositor_domain_priority_ = 0; + + /** This input expects a single value and can't operate on non-single values. See + * realtime_compositor::InputDescriptor for more information. */ + bool compositor_expects_single_value_ = false; + /** Utility method to make the socket available if there is a straightforward way to do so. */ std::function<void(bNode &)> make_available_fn_; @@ -124,6 +132,9 @@ class SocketDeclaration { InputSocketFieldType input_field_type() const; const OutputFieldDependency &output_field_dependency() const; + int compositor_domain_priority() const; + bool compositor_expects_single_value() const; + protected: void set_common_flags(bNodeSocket &socket) const; bool matches_common_data(const bNodeSocket &socket) const; @@ -238,6 +249,22 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { return *(Self *)this; } + /** The priority of the input for determining the domain of the node. See + * realtime_compositor::InputDescriptor for more information. */ + Self &compositor_domain_priority(int priority) + { + decl_->compositor_domain_priority_ = priority; + return *(Self *)this; + } + + /** This input expects a single value and can't operate on non-single values. See + * realtime_compositor::InputDescriptor for more information. */ + Self &compositor_expects_single_value(bool value = true) + { + decl_->compositor_expects_single_value_ = value; + return *(Self *)this; + } + /** * Pass a function that sets properties on the node required to make the corresponding socket * available, if it is not available on the default state of the node. The function is allowed to @@ -428,6 +455,16 @@ inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() return output_field_dependency_; } +inline int SocketDeclaration::compositor_domain_priority() const +{ + return compositor_domain_priority_; +} + +inline bool SocketDeclaration::compositor_expects_single_value() const +{ + return compositor_expects_single_value_; +} + inline void SocketDeclaration::make_available(bNode &node) const { if (make_available_fn_) { diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 586d3e36177..786ce88152e 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -404,7 +404,7 @@ DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES",Tra DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "Convert all faces in a mesh to triangular faces") DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "Shorten curves by removing portions at the start or end") DefNode(GeometryNode, GEO_NODE_UV_PACK_ISLANDS, 0, "UV_PACK_ISLANDS", UVPackIslands, "Pack UV Islands", "Scale islands of a UV map and move them so they fill the UV space as much as possible") -DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map islands based on seam edges") +DefNode(GeometryNode, GEO_NODE_UV_UNWRAP, def_geo_uv_unwrap, "UV_UNWRAP", UVUnwrap, "UV Unwrap", "Generate a UV map based on seam edges") DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "Display the input data in the Spreadsheet Editor") DefNode(GeometryNode, GEO_NODE_VOLUME_CUBE, 0, "VOLUME_CUBE", VolumeCube, "Volume Cube", "Generate a dense volume with a field that controls the density at each grid voxel based on its position") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "Generate a mesh on the \"surface\" of a volume") diff --git a/source/blender/nodes/composite/CMakeLists.txt b/source/blender/nodes/composite/CMakeLists.txt index c0100d77889..2537e8e93cc 100644 --- a/source/blender/nodes/composite/CMakeLists.txt +++ b/source/blender/nodes/composite/CMakeLists.txt @@ -10,11 +10,14 @@ set(INC ../../blenlib ../../blentranslation ../../depsgraph + ../../functions + ../../gpu ../../imbuf ../../makesdna ../../makesrna ../../render ../../windowmanager + ../../compositor/realtime_compositor ../../../../intern/guardedalloc # dna_type_offsets.h @@ -120,15 +123,19 @@ set(SRC node_composite_util.hh ) +set(LIB + bf_realtime_compositor +) + if(WITH_IMAGE_OPENEXR) add_definitions(-DWITH_OPENEXR) endif() -if(WITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) list(APPEND INC ../../compositor ) - add_definitions(-DWITH_COMPOSITOR) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_OPENIMAGEDENOISE) diff --git a/source/blender/nodes/composite/node_composite_tree.cc b/source/blender/nodes/composite/node_composite_tree.cc index 32b5d98a556..9792c55b590 100644 --- a/source/blender/nodes/composite/node_composite_tree.cc +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -32,7 +32,7 @@ #include "NOD_composite.h" #include "node_composite_util.hh" -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU # include "COM_compositor.h" #endif @@ -183,6 +183,7 @@ void register_node_tree_type_cmp() tt->type = NTREE_COMPOSIT; strcpy(tt->idname, "CompositorNodeTree"); + strcpy(tt->group_idname, "CompositorNodeGroup"); strcpy(tt->ui_name, N_("Compositor")); tt->ui_icon = ICON_NODE_COMPOSITING; strcpy(tt->ui_description, N_("Compositing nodes")); @@ -209,7 +210,7 @@ void ntreeCompositExecTree(Scene *scene, int do_preview, const char *view_name) { -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU COM_execute(rd, scene, ntree, rendering, view_name); #else UNUSED_VARS(scene, ntree, rd, rendering, view_name); diff --git a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc index d392b810bc1..64c59eb24e3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc +++ b/source/blender/nodes/composite/nodes/node_composite_alpha_over.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** ALPHAOVER ******************** */ @@ -16,9 +20,18 @@ namespace blender::nodes::node_composite_alpha_over_cc { static void cmp_node_alphaover_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(2); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Image"), "Image_001") + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -36,6 +49,52 @@ static void node_composit_buts_alphaover(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "premul", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class AlphaOverShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float premultiply_factor = get_premultiply_factor(); + if (premultiply_factor != 0.0f) { + GPU_stack_link(material, + &bnode(), + "node_composite_alpha_over_mixed", + inputs, + outputs, + GPU_uniform(&premultiply_factor)); + return; + } + + if (get_use_premultiply()) { + GPU_stack_link(material, &bnode(), "node_composite_alpha_over_key", inputs, outputs); + return; + } + + GPU_stack_link(material, &bnode(), "node_composite_alpha_over_premultiply", inputs, outputs); + } + + bool get_use_premultiply() + { + return bnode().custom1; + } + + float get_premultiply_factor() + { + return ((NodeTwoFloats *)bnode().storage)->x; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new AlphaOverShaderNode(node); +} + } // namespace blender::nodes::node_composite_alpha_over_cc void register_node_type_cmp_alphaover() @@ -50,6 +109,7 @@ void register_node_type_cmp_alphaover() node_type_init(&ntype, file_ns::node_alphaover_init); node_type_storage( &ntype, "NodeTwoFloats", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc index f45b678fc50..55fe3366526 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Anti-Aliasing (SMAA 1x) ******************** */ @@ -42,6 +44,23 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class AntiAliasingOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new AntiAliasingOperation(context, node); +} + } // namespace blender::nodes::node_composite_antialiasing_cc void register_node_type_cmp_antialiasing() @@ -58,6 +77,7 @@ void register_node_type_cmp_antialiasing() node_type_init(&ntype, file_ns::node_composit_init_antialiasing); node_type_storage( &ntype, "NodeAntiAliasingData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc index ad4a1f701d6..66a321eb088 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** BILATERALBLUR ******************** */ @@ -42,6 +44,23 @@ static void node_composit_buts_bilateralblur(uiLayout *layout, uiItemR(col, ptr, "sigma_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BilateralBlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BilateralBlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_bilateralblur_cc void register_node_type_cmp_bilateralblur() @@ -56,6 +75,7 @@ void register_node_type_cmp_bilateralblur() node_type_init(&ntype, file_ns::node_composit_init_bilateralblur); node_type_storage( &ntype, "NodeBilateralBlurData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.cc b/source/blender/nodes/composite/nodes/node_composite_blur.cc index 7beffe15c8e..cb1d93fe10b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_blur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** BLUR ******************** */ @@ -71,6 +73,23 @@ static void node_composit_buts_blur(uiLayout *layout, bContext *UNUSED(C), Point uiItemR(col, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_blur_cc void register_node_type_cmp_blur() @@ -86,6 +105,7 @@ void register_node_type_cmp_blur() node_type_init(&ntype, file_ns::node_composit_init_blur); node_type_storage( &ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index a936bafe671..538f00af12d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** BLUR ******************** */ @@ -37,6 +39,23 @@ static void node_composit_buts_bokehblur(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "use_extended_bounds", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BokehBlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BokehBlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_bokehblur_cc void register_node_type_cmp_bokehblur() @@ -49,6 +68,7 @@ void register_node_type_cmp_bokehblur() ntype.declare = file_ns::cmp_node_bokehblur_declare; ntype.draw_buttons = file_ns::node_composit_buts_bokehblur; node_type_init(&ntype, file_ns::node_composit_init_bokehblur); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index 8330c56736a..a11cba37191 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Bokeh image Tools ******************** */ @@ -45,6 +47,23 @@ static void node_composit_buts_bokehimage(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "shift", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BokehImageOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Image").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BokehImageOperation(context, node); +} + } // namespace blender::nodes::node_composite_bokehimage_cc void register_node_type_cmp_bokehimage() @@ -60,6 +79,7 @@ void register_node_type_cmp_bokehimage() node_type_init(&ntype, file_ns::node_composit_init_bokehimage); node_type_storage( &ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc index f39b69c63f2..9c7bb6432cb 100644 --- a/source/blender/nodes/composite/nodes/node_composite_boxmask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include <cmath> + +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ @@ -48,6 +57,98 @@ static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BoxMaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUShader *shader = shader_manager().get(get_shader_name()); + GPU_shader_bind(shader); + + const Domain domain = compute_domain(); + + GPU_shader_uniform_2iv(shader, "domain_size", domain.size); + + GPU_shader_uniform_2fv(shader, "location", get_location()); + GPU_shader_uniform_2fv(shader, "size", get_size() / 2.0f); + GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle())); + GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle())); + + const Result &input_mask = get_input("Mask"); + input_mask.bind_as_texture(shader, "base_mask_tx"); + + const Result &value = get_input("Value"); + value.bind_as_texture(shader, "mask_value_tx"); + + Result &output_mask = get_result("Mask"); + output_mask.allocate_texture(domain); + output_mask.bind_as_image(shader, "output_mask_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_mask.unbind_as_texture(); + value.unbind_as_texture(); + output_mask.unbind_as_image(); + GPU_shader_unbind(); + } + + Domain compute_domain() override + { + if (get_input("Mask").is_single_value()) { + return Domain(context().get_output_size()); + } + return get_input("Mask").domain(); + } + + CMPNodeMaskType get_mask_type() + { + return (CMPNodeMaskType)bnode().custom1; + } + + const char *get_shader_name() + { + switch (get_mask_type()) { + default: + case CMP_NODE_MASKTYPE_ADD: + return "compositor_box_mask_add"; + case CMP_NODE_MASKTYPE_SUBTRACT: + return "compositor_box_mask_subtract"; + case CMP_NODE_MASKTYPE_MULTIPLY: + return "compositor_box_mask_multiply"; + case CMP_NODE_MASKTYPE_NOT: + return "compositor_box_mask_not"; + } + } + + NodeBoxMask &get_node_box_mask() + { + return *static_cast<NodeBoxMask *>(bnode().storage); + } + + float2 get_location() + { + return float2(get_node_box_mask().x, get_node_box_mask().y); + } + + float2 get_size() + { + return float2(get_node_box_mask().width, get_node_box_mask().height); + } + + float get_angle() + { + return get_node_box_mask().rotation; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new BoxMaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_boxmask_cc void register_node_type_cmp_boxmask() @@ -61,6 +162,7 @@ void register_node_type_cmp_boxmask() ntype.draw_buttons = file_ns::node_composit_buts_boxmask; node_type_init(&ntype, file_ns::node_composit_init_boxmask); node_type_storage(&ntype, "NodeBoxMask", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.cc b/source/blender/nodes/composite/nodes/node_composite_brightness.cc index 65ed2885d9b..fa22f551de6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.cc +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Bright and Contrast ******************** */ @@ -16,9 +20,11 @@ namespace blender::nodes::node_composite_brightness_cc { static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f); - b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Bright")).min(-100.0f).max(100.0f).compositor_domain_priority(1); + b.add_input<decl::Float>(N_("Contrast")).min(-100.0f).max(100.0f).compositor_domain_priority(2); b.add_output<decl::Color>(N_("Image")); } @@ -34,6 +40,38 @@ static void node_composit_buts_brightcontrast(uiLayout *layout, uiItemR(layout, ptr, "use_premultiply", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class BrightContrastShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float use_premultiply = get_use_premultiply(); + + GPU_stack_link(material, + &bnode(), + "node_composite_bright_contrast", + inputs, + outputs, + GPU_constant(&use_premultiply)); + } + + bool get_use_premultiply() + { + return bnode().custom1; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new BrightContrastShaderNode(node); +} + } // namespace blender::nodes::node_composite_brightness_cc void register_node_type_cmp_brightcontrast() @@ -46,6 +84,7 @@ void register_node_type_cmp_brightcontrast() ntype.declare = file_ns::cmp_node_brightcontrast_declare; ntype.draw_buttons = file_ns::node_composit_buts_brightcontrast; node_type_init(&ntype, file_ns::node_composit_init_brightcontrast); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc index 627f07fdfce..018632f776c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_channel_matte.cc @@ -10,6 +10,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Channel Matte Node ********************************* */ @@ -18,7 +22,9 @@ namespace blender::nodes::node_composite_channel_matte_cc { static void cmp_node_channel_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -79,6 +85,96 @@ static void node_composit_buts_channel_matte(uiLayout *layout, col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ChannelMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float color_space = get_color_space(); + const float matte_channel = get_matte_channel(); + float limit_channels[2]; + get_limit_channels(limit_channels); + const float max_limit = get_max_limit(); + const float min_limit = get_min_limit(); + + GPU_stack_link(material, + &bnode(), + "node_composite_channel_matte", + inputs, + outputs, + GPU_constant(&color_space), + GPU_constant(&matte_channel), + GPU_constant(limit_channels), + GPU_uniform(&max_limit), + GPU_uniform(&min_limit)); + } + + /* 1 -> CMP_NODE_CHANNEL_MATTE_CS_RGB + * 2 -> CMP_NODE_CHANNEL_MATTE_CS_HSV + * 3 -> CMP_NODE_CHANNEL_MATTE_CS_YUV + * 4 -> CMP_NODE_CHANNEL_MATTE_CS_YCC */ + int get_color_space() + { + return bnode().custom1; + } + + /* Get the index of the channel used to generate the matte. */ + int get_matte_channel() + { + return bnode().custom2 - 1; + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + /* Get the index of the channel used to compute the limit value. */ + int get_limit_channel() + { + return get_node_chroma()->channel - 1; + } + + /* Get the indices of the channels used to compute the limit value. We always assume the limit + * algorithm is Max, if it is a single limit channel, store it in both limit channels, because + * the maximum of two identical values is the same value. */ + void get_limit_channels(float limit_channels[2]) + { + if (get_node_chroma()->algorithm == CMP_NODE_CHANNEL_MATTE_LIMIT_ALGORITHM_MAX) { + /* If the algorithm is Max, store the indices of the other two channels other than the matte + * channel. */ + limit_channels[0] = (get_matte_channel() + 1) % 3; + limit_channels[1] = (get_matte_channel() + 2) % 3; + } + else { + /* If the algorithm is Single, store the index of the limit channel in both channels. */ + limit_channels[0] = get_limit_channel(); + limit_channels[1] = get_limit_channel(); + } + } + + float get_max_limit() + { + return get_node_chroma()->t1; + } + + float get_min_limit() + { + return get_node_chroma()->t2; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ChannelMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_channel_matte_cc void register_node_type_cmp_channel_matte() @@ -93,6 +189,7 @@ void register_node_type_cmp_channel_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_channel_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc index 69319c6825d..cb3648c5680 100644 --- a/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_chroma_matte.cc @@ -5,11 +5,17 @@ * \ingroup cmpnodes */ +#include <cmath> + #include "BLI_math_rotation.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Chroma Key ********************************************************** */ @@ -18,8 +24,12 @@ namespace blender::nodes::node_composite_chroma_matte_cc { static void cmp_node_chroma_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Key Color")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -51,6 +61,57 @@ static void node_composit_buts_chroma_matte(uiLayout *layout, bContext *UNUSED(C // uiItemR(col, ptr, "shadow_adjust", UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ChromaMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float acceptance = get_acceptance(); + const float cutoff = get_cutoff(); + const float falloff = get_falloff(); + + GPU_stack_link(material, + &bnode(), + "node_composite_chroma_matte", + inputs, + outputs, + GPU_uniform(&acceptance), + GPU_uniform(&cutoff), + GPU_uniform(&falloff)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + float get_acceptance() + { + return std::tan(get_node_chroma()->t1) / 2.0f; + } + + float get_cutoff() + { + return get_node_chroma()->t2; + } + + float get_falloff() + { + return get_node_chroma()->fstrength; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ChromaMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_chroma_matte_cc void register_node_type_cmp_chroma_matte() @@ -65,6 +126,7 @@ void register_node_type_cmp_chroma_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_chroma_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc index 474fb1b72f2..5e3aaf512e6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_color_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_color_matte.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Color Matte ********************************************************** */ @@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_color_matte_cc { static void cmp_node_color_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Key Color")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -50,6 +58,58 @@ static void node_composit_buts_color_matte(uiLayout *layout, bContext *UNUSED(C) col, ptr, "color_value", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ColorMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float hue_epsilon = get_hue_epsilon(); + const float saturation_epsilon = get_saturation_epsilon(); + const float value_epsilon = get_value_epsilon(); + + GPU_stack_link(material, + &bnode(), + "node_composite_color_matte", + inputs, + outputs, + GPU_uniform(&hue_epsilon), + GPU_uniform(&saturation_epsilon), + GPU_uniform(&value_epsilon)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + float get_hue_epsilon() + { + /* Divide by 2 because the hue wraps around. */ + return get_node_chroma()->t1 / 2.0f; + } + + float get_saturation_epsilon() + { + return get_node_chroma()->t2; + } + + float get_value_epsilon() + { + return get_node_chroma()->t3; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_color_matte_cc void register_node_type_cmp_color_matte() @@ -64,6 +124,7 @@ void register_node_type_cmp_color_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_color_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc index 9ad5dfbaeb2..9744c01a256 100644 --- a/source/blender/nodes/composite/nodes/node_composite_color_spill.cc +++ b/source/blender/nodes/composite/nodes/node_composite_color_spill.cc @@ -10,6 +10,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Color Spill Suppression ********************************* */ @@ -18,8 +22,15 @@ namespace blender::nodes::node_composite_color_spill_cc { static void cmp_node_color_spill_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -27,8 +38,8 @@ static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node { NodeColorspill *ncs = MEM_cnew<NodeColorspill>(__func__); node->storage = ncs; + node->custom2 = CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_SINGLE; node->custom1 = 2; /* green channel */ - node->custom2 = 0; /* simple limit algorithm */ ncs->limchan = 0; /* limit by red */ ncs->limscale = 1.0f; /* limit scaling factor */ ncs->unspill = 0; /* do not use unspill */ @@ -80,6 +91,103 @@ static void node_composit_buts_color_spill(uiLayout *layout, bContext *UNUSED(C) } } +using namespace blender::realtime_compositor; + +class ColorSpillShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float spill_channel = get_spill_channel(); + float spill_scale[3]; + get_spill_scale(spill_scale); + float limit_channels[2]; + get_limit_channels(limit_channels); + const float limit_scale = get_limit_scale(); + + GPU_stack_link(material, + &bnode(), + "node_composite_color_spill", + inputs, + outputs, + GPU_constant(&spill_channel), + GPU_uniform(spill_scale), + GPU_constant(limit_channels), + GPU_uniform(&limit_scale)); + } + + /* Get the index of the channel used for spilling. */ + int get_spill_channel() + { + return bnode().custom1 - 1; + } + + CMPNodeColorSpillLimitAlgorithm get_limit_algorithm() + { + return (CMPNodeColorSpillLimitAlgorithm)bnode().custom2; + } + + NodeColorspill *get_node_color_spill() + { + return static_cast<NodeColorspill *>(bnode().storage); + } + + void get_spill_scale(float spill_scale[3]) + { + const NodeColorspill *node_color_spill = get_node_color_spill(); + if (node_color_spill->unspill) { + spill_scale[0] = node_color_spill->uspillr; + spill_scale[1] = node_color_spill->uspillg; + spill_scale[2] = node_color_spill->uspillb; + spill_scale[get_spill_channel()] *= -1.0f; + } + else { + spill_scale[0] = 0.0f; + spill_scale[1] = 0.0f; + spill_scale[2] = 0.0f; + spill_scale[get_spill_channel()] = -1.0f; + } + } + + /* Get the index of the channel used for limiting. */ + int get_limit_channel() + { + return get_node_color_spill()->limchan; + } + + /* Get the indices of the channels used to compute the limit value. We always assume the limit + * algorithm is Average, if it is a single limit channel, store it in both limit channels, + * because the average of two identical values is the same value. */ + void get_limit_channels(float limit_channels[2]) + { + if (get_limit_algorithm() == CMP_NODE_COLOR_SPILL_LIMIT_ALGORITHM_AVERAGE) { + /* If the algorithm is Average, store the indices of the other two channels other than the + * spill channel. */ + limit_channels[0] = (get_spill_channel() + 1) % 3; + limit_channels[1] = (get_spill_channel() + 2) % 3; + } + else { + /* If the algorithm is Single, store the index of the limit channel in both channels. */ + limit_channels[0] = get_limit_channel(); + limit_channels[1] = get_limit_channel(); + } + } + + float get_limit_scale() + { + return get_node_color_spill()->limscale; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorSpillShaderNode(node); +} + } // namespace blender::nodes::node_composite_color_spill_cc void register_node_type_cmp_color_spill() @@ -94,6 +202,7 @@ void register_node_type_cmp_color_spill() node_type_init(&ntype, file_ns::node_composit_init_color_spill); node_type_storage( &ntype, "NodeColorspill", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index dd081c8fc12..95675169c76 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -10,6 +10,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Color Balance ********************************* */ @@ -46,8 +50,15 @@ namespace blender::nodes::node_composite_colorbalance_cc { static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -71,7 +82,7 @@ static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); - if (RNA_enum_get(ptr, "correction_method") == 0) { + if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) { split = uiLayoutSplit(layout, 0.0f, false); col = uiLayoutColumn(split, false); @@ -116,7 +127,7 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout, { uiItemR(layout, ptr, "correction_method", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); - if (RNA_enum_get(ptr, "correction_method") == 0) { + if (RNA_enum_get(ptr, "correction_method") == CMP_NODE_COLOR_BALANCE_LGG) { uiTemplateColorPicker(layout, ptr, "lift", true, true, false, true); uiItemR(layout, ptr, "lift", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); @@ -139,6 +150,58 @@ static void node_composit_buts_colorbalance_ex(uiLayout *layout, } } +using namespace blender::realtime_compositor; + +class ColorBalanceShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const NodeColorBalance *node_color_balance = get_node_color_balance(); + + if (get_color_balance_method() == CMP_NODE_COLOR_BALANCE_LGG) { + GPU_stack_link(material, + &bnode(), + "node_composite_color_balance_lgg", + inputs, + outputs, + GPU_uniform(node_color_balance->lift), + GPU_uniform(node_color_balance->gamma), + GPU_uniform(node_color_balance->gain)); + return; + } + + GPU_stack_link(material, + &bnode(), + "node_composite_color_balance_asc_cdl", + inputs, + outputs, + GPU_uniform(node_color_balance->offset), + GPU_uniform(node_color_balance->power), + GPU_uniform(node_color_balance->slope), + GPU_uniform(&node_color_balance->offset_basis)); + } + + CMPNodeColorBalanceMethod get_color_balance_method() + { + return (CMPNodeColorBalanceMethod)bnode().custom1; + } + + NodeColorBalance *get_node_color_balance() + { + return static_cast<NodeColorBalance *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorBalanceShaderNode(node); +} + } // namespace blender::nodes::node_composite_colorbalance_cc void register_node_type_cmp_colorbalance() @@ -155,6 +218,7 @@ void register_node_type_cmp_colorbalance() node_type_init(&ntype, file_ns::node_composit_init_colorbalance); node_type_storage( &ntype, "NodeColorBalance", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 39ecd277cec..36e6672ce1c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -5,9 +5,15 @@ * \ingroup cmpnodes */ +#include "IMB_colormanagement.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Color Correction ********************************* */ @@ -16,8 +22,14 @@ namespace blender::nodes::node_composite_colorcorrection_cc { static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Mask")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -266,6 +278,73 @@ static void node_composit_buts_colorcorrection_ex(uiLayout *layout, uiItemR(row, ptr, "midtones_end", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ColorCorrectionShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + float enabled_channels[3]; + get_enabled_channels(enabled_channels); + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + + const NodeColorCorrection *node_color_correction = get_node_color_correction(); + + GPU_stack_link(material, + &bnode(), + "node_composite_color_correction", + inputs, + outputs, + GPU_constant(enabled_channels), + GPU_uniform(&node_color_correction->startmidtones), + GPU_uniform(&node_color_correction->endmidtones), + GPU_uniform(&node_color_correction->master.saturation), + GPU_uniform(&node_color_correction->master.contrast), + GPU_uniform(&node_color_correction->master.gamma), + GPU_uniform(&node_color_correction->master.gain), + GPU_uniform(&node_color_correction->master.lift), + GPU_uniform(&node_color_correction->shadows.saturation), + GPU_uniform(&node_color_correction->shadows.contrast), + GPU_uniform(&node_color_correction->shadows.gamma), + GPU_uniform(&node_color_correction->shadows.gain), + GPU_uniform(&node_color_correction->shadows.lift), + GPU_uniform(&node_color_correction->midtones.saturation), + GPU_uniform(&node_color_correction->midtones.contrast), + GPU_uniform(&node_color_correction->midtones.gamma), + GPU_uniform(&node_color_correction->midtones.gain), + GPU_uniform(&node_color_correction->midtones.lift), + GPU_uniform(&node_color_correction->highlights.saturation), + GPU_uniform(&node_color_correction->highlights.contrast), + GPU_uniform(&node_color_correction->highlights.gamma), + GPU_uniform(&node_color_correction->highlights.gain), + GPU_uniform(&node_color_correction->highlights.lift), + GPU_constant(luminance_coefficients)); + } + + void get_enabled_channels(float enabled_channels[3]) + { + for (int i = 0; i < 3; i++) { + enabled_channels[i] = (bnode().custom1 & (1 << i)) ? 1.0f : 0.0f; + } + } + + NodeColorCorrection *get_node_color_correction() + { + return static_cast<NodeColorCorrection *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorCorrectionShaderNode(node); +} + } // namespace blender::nodes::node_composite_colorcorrection_cc void register_node_type_cmp_colorcorrection() @@ -282,6 +361,7 @@ void register_node_type_cmp_colorcorrection() node_type_init(&ntype, file_ns::node_composit_init_colorcorrection); node_type_storage( &ntype, "NodeColorCorrection", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.cc b/source/blender/nodes/composite/nodes/node_composite_composite.cc index d35ce7dc11a..68061bb434d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.cc +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** COMPOSITE ******************** */ @@ -26,6 +35,125 @@ static void node_composit_buts_composite(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "use_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class CompositeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + if (image.is_single_value() && alpha.is_single_value()) { + execute_clear(); + } + else if (ignore_alpha()) { + execute_ignore_alpha(); + } + else if (!node().input_by_identifier("Alpha")->is_logically_linked()) { + execute_copy(); + } + else { + execute_set_alpha(); + } + } + + /* Executes when all inputs are single values, in which case, the output texture can just be + * cleared to the appropriate color. */ + void execute_clear() + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + float4 color = image.get_color_value(); + if (ignore_alpha()) { + color.w = 1.0f; + } + else if (node().input_by_identifier("Alpha")->is_logically_linked()) { + color.w = alpha.get_float_value(); + } + + GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color); + } + + /* Executes when the alpha channel of the image is ignored. */ + void execute_ignore_alpha() + { + GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "input_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* Executes when the image texture is written with no adjustments and can thus be copied directly + * to the output texture. */ + void execute_copy() + { + const Result &image = get_input("Image"); + + /* Make sure any prior writes to the texture are reflected before copying it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + GPU_texture_copy(context().get_output_texture(), image.texture()); + } + + /* Executes when the alpha channel of the image is set as the value of the input alpha. */ + void execute_set_alpha() + { + GPUShader *shader = shader_manager().get("compositor_set_alpha"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "image_tx"); + + const Result &alpha = get_input("Alpha"); + alpha.bind_as_texture(shader, "alpha_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + alpha.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the + * alpha channel of the image is retained, but only if the alpha input is not linked. If the + * alpha input is linked, it the value of that input will be used as the alpha of the image. */ + bool ignore_alpha() + { + return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA; + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CompositeOperation(context, node); +} + } // namespace blender::nodes::node_composite_composite_cc void register_node_type_cmp_composite() @@ -37,6 +165,7 @@ void register_node_type_cmp_composite() cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT); ntype.declare = file_ns::cmp_node_composite_declare; ntype.draw_buttons = file_ns::node_composit_buts_composite; + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.flag |= NODE_PREVIEW; ntype.no_muting = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc index 303248c3852..e36da39cca1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc +++ b/source/blender/nodes/composite/nodes/node_composite_convert_color_space.cc @@ -14,6 +14,8 @@ #include "IMB_colormanagement.h" +#include "COM_node_operation.hh" + namespace blender::nodes::node_composite_convert_color_space_cc { static void CMP_NODE_CONVERT_COLOR_SPACE_declare(NodeDeclarationBuilder &b) @@ -47,6 +49,23 @@ static void node_composit_buts_convert_colorspace(uiLayout *layout, uiItemR(layout, ptr, "to_color_space", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ConvertColorSpaceOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ConvertColorSpaceOperation(context, node); +} + } // namespace blender::nodes::node_composite_convert_color_space_cc void register_node_type_cmp_convert_color_space(void) @@ -62,6 +81,7 @@ void register_node_type_cmp_convert_color_space(void) node_type_init(&ntype, file_ns::node_composit_init_convert_colorspace); node_type_storage( &ntype, "NodeConvertColorSpace", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc index 07da0da0be1..9679701a7cf 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_cornerpin_cc { @@ -32,6 +34,24 @@ static void cmp_node_cornerpin_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Plane")); } +using namespace blender::realtime_compositor; + +class CornerPinOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Plane").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CornerPinOperation(context, node); +} + } // namespace blender::nodes::node_composite_cornerpin_cc void register_node_type_cmp_cornerpin() @@ -42,6 +62,7 @@ void register_node_type_cmp_cornerpin() cmp_node_type_base(&ntype, CMP_NODE_CORNERPIN, "Corner Pin", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_cornerpin_declare; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.cc b/source/blender/nodes/composite/nodes/node_composite_crop.cc index 823e1052dd0..d7331732fc7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_crop.cc +++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc @@ -5,11 +5,22 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" + +#include "DNA_node_types.h" + #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** Crop ******************** */ @@ -18,7 +29,9 @@ namespace blender::nodes::node_composite_crop_cc { static void cmp_node_crop_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -54,6 +67,161 @@ static void node_composit_buts_crop(uiLayout *layout, bContext *UNUSED(C), Point } } +using namespace blender::realtime_compositor; + +class CropOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + /* The operation does nothing, so just pass the input through. */ + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + if (get_is_image_crop()) { + execute_image_crop(); + } + else { + execute_alpha_crop(); + } + } + + /* Crop by replacing areas outside of the cropping bounds with zero alpha. The output have the + * same domain as the input image. */ + void execute_alpha_crop() + { + GPUShader *shader = shader_manager().get("compositor_alpha_crop"); + GPU_shader_bind(shader); + + int2 lower_bound, upper_bound; + compute_cropping_bounds(lower_bound, upper_bound); + GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound); + GPU_shader_uniform_2iv(shader, "upper_bound", upper_bound); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + /* Crop the image into a new size that matches the cropping bounds. */ + void execute_image_crop() + { + int2 lower_bound, upper_bound; + compute_cropping_bounds(lower_bound, upper_bound); + + /* The image is cropped into nothing, so just return a single zero value. */ + if (lower_bound.x == upper_bound.x || lower_bound.y == upper_bound.y) { + Result &result = get_result("Image"); + result.allocate_invalid(); + return; + } + + GPUShader *shader = shader_manager().get("compositor_image_crop"); + GPU_shader_bind(shader); + + GPU_shader_uniform_2iv(shader, "lower_bound", lower_bound); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + const int2 size = upper_bound - lower_bound; + + Result &output_image = get_result("Image"); + output_image.allocate_texture(Domain(size, compute_domain().transformation)); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + /* If true, the image should actually be cropped into a new size. Otherwise, if false, the region + * outside of the cropping bounds will be set to a zero alpha value. */ + bool get_is_image_crop() + { + return bnode().custom1; + } + + bool get_is_relative() + { + return bnode().custom2; + } + + NodeTwoXYs &get_node_two_xys() + { + return *static_cast<NodeTwoXYs *>(bnode().storage); + } + + /* Returns true if the operation does nothing and the input can be passed through. */ + bool is_identity() + { + const Result &input = get_input("Image"); + /* Single value inputs can't be cropped and are returned as is. */ + if (input.is_single_value()) { + return true; + } + + int2 lower_bound, upper_bound; + compute_cropping_bounds(lower_bound, upper_bound); + const int2 input_size = input.domain().size; + /* The cropping bounds cover the whole image, so no cropping happens. */ + if (lower_bound == int2(0) && upper_bound == input_size) { + return true; + } + + return false; + } + + void compute_cropping_bounds(int2 &lower_bound, int2 &upper_bound) + { + const NodeTwoXYs &node_two_xys = get_node_two_xys(); + const int2 input_size = get_input("Image").domain().size; + + if (get_is_relative()) { + /* The cropping bounds are relative to the image size. The factors are in the [0, 1] range, + * so it is guaranteed that they won't go over the input image size. */ + lower_bound.x = input_size.x * node_two_xys.fac_x1; + lower_bound.y = input_size.y * node_two_xys.fac_y2; + upper_bound.x = input_size.x * node_two_xys.fac_x2; + upper_bound.y = input_size.y * node_two_xys.fac_y1; + } + else { + /* Make sure the bounds don't go over the input image size. */ + lower_bound.x = min_ii(node_two_xys.x1, input_size.x); + lower_bound.y = min_ii(node_two_xys.y2, input_size.y); + upper_bound.x = min_ii(node_two_xys.x2, input_size.x); + upper_bound.y = min_ii(node_two_xys.y1, input_size.y); + } + + /* Make sure upper bound is actually higher than the lower bound. */ + lower_bound.x = min_ii(lower_bound.x, upper_bound.x); + lower_bound.y = min_ii(lower_bound.y, upper_bound.y); + upper_bound.x = max_ii(lower_bound.x, upper_bound.x); + upper_bound.y = max_ii(lower_bound.y, upper_bound.y); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CropOperation(context, node); +} + } // namespace blender::nodes::node_composite_crop_cc void register_node_type_cmp_crop() @@ -67,6 +235,7 @@ void register_node_type_cmp_crop() ntype.draw_buttons = file_ns::node_composit_buts_crop; node_type_init(&ntype, file_ns::node_composit_init_crop); node_type_storage(&ntype, "NodeTwoXYs", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 2d362a39814..7e5544381a4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -26,6 +26,8 @@ #include "RE_pipeline.h" +#include "COM_node_operation.hh" + #include <optional> /* -------------------------------------------------------------------- */ @@ -105,7 +107,6 @@ static blender::bke::cryptomatte::CryptomatteSessionPtr cryptomatte_init_from_no return session; } -extern "C" { static CryptomatteEntry *cryptomatte_find(const NodeCryptomatte &n, float encoded_hash) { LISTBASE_FOREACH (CryptomatteEntry *, entry, &n.entries) { @@ -299,6 +300,25 @@ static bool node_poll_cryptomatte(bNodeType *UNUSED(ntype), return false; } +using namespace blender::realtime_compositor; + +class CryptoMatteOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Matte").allocate_invalid(); + get_result("Pick").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CryptoMatteOperation(context, node); +} + } // namespace blender::nodes::node_composite_cryptomatte_cc void register_node_type_cmp_cryptomatte() @@ -316,6 +336,8 @@ void register_node_type_cmp_cryptomatte() ntype.poll = file_ns::node_poll_cryptomatte; node_type_storage( &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); + ntype.get_compositor_operation = file_ns::get_compositor_operation; + nodeRegisterType(&ntype); } @@ -350,7 +372,7 @@ int ntreeCompositCryptomatteRemoveSocket(bNodeTree *ntree, bNode *node) return 1; } -namespace blender::nodes::node_composite_cryptomatte_cc { +namespace blender::nodes::node_composite_legacy_cryptomatte_cc { static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node) { @@ -365,24 +387,43 @@ static void node_init_cryptomatte_legacy(bNodeTree *ntree, bNode *node) ntreeCompositCryptomatteAddSocket(ntree, node); } -} // namespace blender::nodes::node_composite_cryptomatte_cc +using namespace blender::realtime_compositor; + +class CryptoMatteOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("image").pass_through(get_result("Image")); + get_result("Matte").allocate_invalid(); + get_result("Pick").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new CryptoMatteOperation(context, node); +} + +} // namespace blender::nodes::node_composite_legacy_cryptomatte_cc void register_node_type_cmp_cryptomatte_legacy() { - namespace legacy_file_ns = blender::nodes::node_composite_cryptomatte_cc; + namespace legacy_file_ns = blender::nodes::node_composite_legacy_cryptomatte_cc; namespace file_ns = blender::nodes::node_composite_cryptomatte_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_CRYPTOMATTE_LEGACY, "Cryptomatte", NODE_CLASS_MATTE); node_type_socket_templates(&ntype, nullptr, file_ns::cmp_node_cryptomatte_out); - node_type_init(&ntype, file_ns::node_init_cryptomatte_legacy); + node_type_init(&ntype, legacy_file_ns::node_init_cryptomatte_legacy); node_type_storage( &ntype, "NodeCryptomatte", file_ns::node_free_cryptomatte, file_ns::node_copy_cryptomatte); ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_operation = legacy_file_ns::get_compositor_operation; nodeRegisterType(&ntype); } /** \} */ -} diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.cc b/source/blender/nodes/composite/nodes/node_composite_curves.cc index 802664d7934..c5d303c576a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.cc +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -5,16 +5,23 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.h" + #include "BKE_colortools.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_node_operation.hh" +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** CURVE Time ******************** */ -namespace blender::nodes::node_composite_curves_cc { +namespace blender::nodes::node_composite_time_curves_cc { static void cmp_node_time_declare(NodeDeclarationBuilder &b) { @@ -29,11 +36,65 @@ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); } -} // namespace blender::nodes::node_composite_curves_cc +using namespace blender::realtime_compositor; + +class TimeCurveOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &result = get_result("Fac"); + result.allocate_single_value(); + + CurveMapping *curve_mapping = get_curve_mapping(); + BKE_curvemapping_init(curve_mapping); + const float time = BKE_curvemapping_evaluateF(curve_mapping, 0, compute_normalized_time()); + result.set_float_value(clamp_f(time, 0.0f, 1.0f)); + } + + CurveMapping *get_curve_mapping() + { + return static_cast<CurveMapping *>(bnode().storage); + } + + int get_start_time() + { + return bnode().custom1; + } + + int get_end_time() + { + return bnode().custom2; + } + + float compute_normalized_time() + { + const int frame_number = context().get_frame_number(); + if (frame_number < get_start_time()) { + return 0.0f; + } + if (frame_number > get_end_time()) { + return 1.0f; + } + if (get_start_time() == get_end_time()) { + return 0.0f; + } + return static_cast<float>(frame_number - get_start_time()) / + static_cast<float>(get_end_time() - get_start_time()); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TimeCurveOperation(context, node); +} + +} // namespace blender::nodes::node_composite_time_curves_cc void register_node_type_cmp_curve_time() { - namespace file_ns = blender::nodes::node_composite_curves_cc; + namespace file_ns = blender::nodes::node_composite_time_curves_cc; static bNodeType ntype; @@ -42,17 +103,22 @@ void register_node_type_cmp_curve_time() node_type_size(&ntype, 200, 140, 320); node_type_init(&ntype, file_ns::node_composit_init_curves_time); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } /* **************** CURVE VEC ******************** */ -namespace blender::nodes::node_composite_curves_cc { +namespace blender::nodes::node_composite_vector_curves_cc { static void cmp_node_curve_vec_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Vector>(N_("Vector")).default_value({0.0f, 0.0f, 0.0f}).min(-1.0f).max(1.0f); + b.add_input<decl::Vector>(N_("Vector")) + .default_value({0.0f, 0.0f, 0.0f}) + .min(-1.0f) + .max(1.0f) + .compositor_domain_priority(0); b.add_output<decl::Vector>(N_("Vector")); } @@ -66,11 +132,63 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false); } -} // namespace blender::nodes::node_composite_curves_cc +using namespace blender::realtime_compositor; + +class VectorCurvesShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + CurveMapping *curve_mapping = get_curve_mapping(); + + 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(material, 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); + + GPU_stack_link(material, + &bnode(), + "curves_vector", + inputs, + outputs, + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); + } + + CurveMapping *get_curve_mapping() + { + return static_cast<CurveMapping *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new VectorCurvesShaderNode(node); +} + +} // namespace blender::nodes::node_composite_vector_curves_cc void register_node_type_cmp_curve_vec() { - namespace file_ns = blender::nodes::node_composite_curves_cc; + namespace file_ns = blender::nodes::node_composite_vector_curves_cc; static bNodeType ntype; @@ -80,19 +198,26 @@ void register_node_type_cmp_curve_vec() node_type_size(&ntype, 200, 140, 320); node_type_init(&ntype, file_ns::node_composit_init_curve_vec); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** CURVE RGB ******************** */ -namespace blender::nodes::node_composite_curves_cc { +namespace blender::nodes::node_composite_rgb_curves_cc { static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype( - PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(-1.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_input<decl::Color>(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); b.add_input<decl::Color>(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); b.add_output<decl::Color>(N_("Image")); @@ -103,11 +228,105 @@ static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node) node->storage = BKE_curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); } -} // namespace blender::nodes::node_composite_curves_cc +using namespace blender::realtime_compositor; + +class RGBCurvesShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + CurveMapping *curve_mapping = get_curve_mapping(); + + 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(material, 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); + + if (curve_mapping->tone == CURVE_TONE_FILMLIKE) { + GPU_stack_link(material, + &bnode(), + "curves_film_like", + inputs, + outputs, + 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; + } + + const float min = 0.0f; + const float max = 1.0f; + GPU_link(material, + "clamp_value", + get_input_link("Fac"), + GPU_constant(&min), + GPU_constant(&max), + &get_input("Fac").link); + + /* 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)) { + GPU_stack_link(material, + &bnode(), + "curves_combined_only", + inputs, + outputs, + 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(material, + &bnode(), + "curves_combined_rgb", + inputs, + outputs, + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers), + GPU_uniform(start_slopes), + GPU_uniform(end_slopes)); + } + + CurveMapping *get_curve_mapping() + { + return static_cast<CurveMapping *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new RGBCurvesShaderNode(node); +} + +} // namespace blender::nodes::node_composite_rgb_curves_cc void register_node_type_cmp_curve_rgb() { - namespace file_ns = blender::nodes::node_composite_curves_cc; + namespace file_ns = blender::nodes::node_composite_rgb_curves_cc; static bNodeType ntype; @@ -116,6 +335,7 @@ void register_node_type_cmp_curve_rgb() node_type_size(&ntype, 200, 140, 320); node_type_init(&ntype, file_ns::node_composit_init_curve_rgb); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.cc b/source/blender/nodes/composite/nodes/node_composite_defocus.cc index 83dd397ff1f..94b4908a1bd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_defocus.cc +++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc @@ -12,6 +12,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* ************ Defocus Node ****************** */ @@ -81,6 +83,23 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA uiItemR(sub, ptr, "z_scale", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DefocusOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DefocusOperation(context, node); +} + } // namespace blender::nodes::node_composite_defocus_cc void register_node_type_cmp_defocus() @@ -94,6 +113,7 @@ void register_node_type_cmp_defocus() ntype.draw_buttons = file_ns::node_composit_buts_defocus; node_type_init(&ntype, file_ns::node_composit_init_defocus); node_type_storage(&ntype, "NodeDefocus", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.cc b/source/blender/nodes/composite/nodes/node_composite_denoise.cc index 051a2580ef9..0452e7cd943 100644 --- a/source/blender/nodes/composite/nodes/node_composite_denoise.cc +++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_denoise_cc { @@ -52,6 +54,23 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "use_hdr", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DenoiseOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DenoiseOperation(context, node); +} + } // namespace blender::nodes::node_composite_denoise_cc void register_node_type_cmp_denoise() @@ -65,6 +84,7 @@ void register_node_type_cmp_denoise() ntype.draw_buttons = file_ns::node_composit_buts_denoise; node_type_init(&ntype, file_ns::node_composit_init_denonise); node_type_storage(&ntype, "NodeDenoise", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc index 66a18cfa369..0b9f9c8f76d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_despeckle.cc +++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** FILTER ******************** */ @@ -36,6 +38,23 @@ static void node_composit_buts_despeckle(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "threshold_neighbor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DespeckleOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DespeckleOperation(context, node); +} + } // namespace blender::nodes::node_composite_despeckle_cc void register_node_type_cmp_despeckle() @@ -49,6 +68,7 @@ void register_node_type_cmp_despeckle() ntype.draw_buttons = file_ns::node_composit_buts_despeckle; ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_despeckle); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc index b87bbe439db..e129dcaa6ef 100644 --- a/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_diff_matte.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* channel Difference Matte ********************************* */ @@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_diff_matte_cc { static void cmp_node_diff_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image 1")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Image 2")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image 1")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Image 2")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -40,6 +48,50 @@ static void node_composit_buts_diff_matte(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DifferenceMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float tolerance = get_tolerance(); + const float falloff = get_falloff(); + + GPU_stack_link(material, + &bnode(), + "node_composite_difference_matte", + inputs, + outputs, + GPU_uniform(&tolerance), + GPU_uniform(&falloff)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + float get_tolerance() + { + return get_node_chroma()->t1; + } + + float get_falloff() + { + return get_node_chroma()->t2; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new DifferenceMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_diff_matte_cc void register_node_type_cmp_diff_matte() @@ -54,6 +106,7 @@ void register_node_type_cmp_diff_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_diff_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.cc b/source/blender/nodes/composite/nodes/node_composite_dilate.cc index 9bdb9ae0837..46199d3ff04 100644 --- a/source/blender/nodes/composite/nodes/node_composite_dilate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Dilate/Erode ******************** */ @@ -43,6 +45,23 @@ static void node_composit_buts_dilateerode(uiLayout *layout, bContext *UNUSED(C) } } +using namespace blender::realtime_compositor; + +class DilateErodeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Mask").pass_through(get_result("Mask")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DilateErodeOperation(context, node); +} + } // namespace blender::nodes::node_composite_dilate_cc void register_node_type_cmp_dilateerode() @@ -57,6 +76,7 @@ void register_node_type_cmp_dilateerode() node_type_init(&ntype, file_ns::node_composit_init_dilateerode); node_type_storage( &ntype, "NodeDilateErode", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc index 3d82ab04fc9..eacba5ad12d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_directionalblur_cc { @@ -51,6 +53,23 @@ static void node_composit_buts_dblur(uiLayout *layout, bContext *UNUSED(C), Poin uiItemR(layout, ptr, "zoom", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DirectionalBlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DirectionalBlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_directionalblur_cc void register_node_type_cmp_dblur() @@ -65,6 +84,7 @@ void register_node_type_cmp_dblur() node_type_init(&ntype, file_ns::node_composit_init_dblur); node_type_storage( &ntype, "NodeDBlurData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.cc b/source/blender/nodes/composite/nodes/node_composite_displace.cc index 0b0d42cbb08..1049f2fa4a9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_displace.cc +++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Displace ******************** */ @@ -24,6 +26,23 @@ static void cmp_node_displace_declare(NodeDeclarationBuilder &b) b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class DisplaceOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DisplaceOperation(context, node); +} + } // namespace blender::nodes::node_composite_displace_cc void register_node_type_cmp_displace() @@ -34,6 +53,7 @@ void register_node_type_cmp_displace() cmp_node_type_base(&ntype, CMP_NODE_DISPLACE, "Displace", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_displace_declare; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc index a8646d8498e..9d910b3f409 100644 --- a/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_distance_matte.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* channel Distance Matte ********************************* */ @@ -16,8 +20,12 @@ namespace blender::nodes::node_composite_distance_matte_cc { static void cmp_node_distance_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Key Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Key Color")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -26,7 +34,7 @@ static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *n { NodeChroma *c = MEM_cnew<NodeChroma>(__func__); node->storage = c; - c->channel = 1; + c->channel = CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA; c->t1 = 0.1f; c->t2 = 0.1f; } @@ -48,6 +56,66 @@ static void node_composit_buts_distance_matte(uiLayout *layout, uiItemR(col, ptr, "falloff", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class DistanceMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float tolerance = get_tolerance(); + const float falloff = get_falloff(); + + if (get_color_space() == CMP_NODE_DISTANCE_MATTE_COLOR_SPACE_RGBA) { + GPU_stack_link(material, + &bnode(), + "node_composite_distance_matte_rgba", + inputs, + outputs, + GPU_uniform(&tolerance), + GPU_uniform(&falloff)); + return; + } + + GPU_stack_link(material, + &bnode(), + "node_composite_distance_matte_ycca", + inputs, + outputs, + GPU_uniform(&tolerance), + GPU_uniform(&falloff)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + CMPNodeDistanceMatteColorSpace get_color_space() + { + return (CMPNodeDistanceMatteColorSpace)get_node_chroma()->channel; + } + + float get_tolerance() + { + return get_node_chroma()->t1; + } + + float get_falloff() + { + return get_node_chroma()->t2; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new DistanceMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_distance_matte_cc void register_node_type_cmp_distance_matte() @@ -62,6 +130,7 @@ void register_node_type_cmp_distance_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_distance_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc index 9dc2b223618..fec7879ed78 100644 --- a/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_double_edge_mask.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Double Edge Mask ******************** */ @@ -35,6 +37,23 @@ static void node_composit_buts_double_edge_mask(uiLayout *layout, uiItemR(col, ptr, "edge_mode", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class DoubleEdgeMaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Inner Mask").pass_through(get_result("Mask")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new DoubleEdgeMaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_double_edge_mask_cc void register_node_type_cmp_doubleedgemask() @@ -46,6 +65,7 @@ void register_node_type_cmp_doubleedgemask() cmp_node_type_base(&ntype, CMP_NODE_DOUBLEEDGEMASK, "Double Edge Mask", NODE_CLASS_MATTE); ntype.declare = file_ns::cmp_node_double_edge_mask_declare; ntype.draw_buttons = file_ns::node_composit_buts_double_edge_mask; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc index 4da6a0a442e..54dfa00eadd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include <cmath> + +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ @@ -46,6 +55,98 @@ static void node_composit_buts_ellipsemask(uiLayout *layout, bContext *UNUSED(C) uiItemR(layout, ptr, "mask_type", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class EllipseMaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUShader *shader = shader_manager().get(get_shader_name()); + GPU_shader_bind(shader); + + const Domain domain = compute_domain(); + + GPU_shader_uniform_2iv(shader, "domain_size", domain.size); + + GPU_shader_uniform_2fv(shader, "location", get_location()); + GPU_shader_uniform_2fv(shader, "radius", get_size() / 2.0f); + GPU_shader_uniform_1f(shader, "cos_angle", std::cos(get_angle())); + GPU_shader_uniform_1f(shader, "sin_angle", std::sin(get_angle())); + + const Result &input_mask = get_input("Mask"); + input_mask.bind_as_texture(shader, "base_mask_tx"); + + const Result &value = get_input("Value"); + value.bind_as_texture(shader, "mask_value_tx"); + + Result &output_mask = get_result("Mask"); + output_mask.allocate_texture(domain); + output_mask.bind_as_image(shader, "output_mask_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_mask.unbind_as_texture(); + value.unbind_as_texture(); + output_mask.unbind_as_image(); + GPU_shader_unbind(); + } + + Domain compute_domain() override + { + if (get_input("Mask").is_single_value()) { + return Domain(context().get_output_size()); + } + return get_input("Mask").domain(); + } + + CMPNodeMaskType get_mask_type() + { + return (CMPNodeMaskType)bnode().custom1; + } + + const char *get_shader_name() + { + switch (get_mask_type()) { + default: + case CMP_NODE_MASKTYPE_ADD: + return "compositor_ellipse_mask_add"; + case CMP_NODE_MASKTYPE_SUBTRACT: + return "compositor_ellipse_mask_subtract"; + case CMP_NODE_MASKTYPE_MULTIPLY: + return "compositor_ellipse_mask_multiply"; + case CMP_NODE_MASKTYPE_NOT: + return "compositor_ellipse_mask_not"; + } + } + + NodeEllipseMask &get_node_ellipse_mask() + { + return *static_cast<NodeEllipseMask *>(bnode().storage); + } + + float2 get_location() + { + return float2(get_node_ellipse_mask().x, get_node_ellipse_mask().y); + } + + float2 get_size() + { + return float2(get_node_ellipse_mask().width, get_node_ellipse_mask().height); + } + + float get_angle() + { + return get_node_ellipse_mask().rotation; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new EllipseMaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_ellipsemask_cc void register_node_type_cmp_ellipsemask() @@ -61,6 +162,7 @@ void register_node_type_cmp_ellipsemask() node_type_init(&ntype, file_ns::node_composit_init_ellipsemask); node_type_storage( &ntype, "NodeEllipseMask", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.cc b/source/blender/nodes/composite/nodes/node_composite_exposure.cc index 881cfc11058..19b93680ff6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_exposure.cc +++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Exposure ******************** */ @@ -13,11 +17,33 @@ namespace blender::nodes::node_composite_exposure_cc { static void cmp_node_exposure_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Exposure")).min(-10.0f).max(10.0f).compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class ExposureShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_exposure", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ExposureShaderNode(node); +} + } // namespace blender::nodes::node_composite_exposure_cc void register_node_type_cmp_exposure() @@ -28,6 +54,7 @@ void register_node_type_cmp_exposure() cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_exposure_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.cc b/source/blender/nodes/composite/nodes/node_composite_filter.cc index c343c21feb2..854cf684806 100644 --- a/source/blender/nodes/composite/nodes/node_composite_filter.cc +++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** FILTER ******************** */ @@ -26,6 +28,23 @@ static void node_composit_buts_filter(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class FilterOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new FilterOperation(context, node); +} + } // namespace blender::nodes::node_composite_filter_cc void register_node_type_cmp_filter() @@ -39,6 +58,7 @@ void register_node_type_cmp_filter() ntype.draw_buttons = file_ns::node_composit_buts_filter; ntype.labelfunc = node_filter_label; ntype.flag |= NODE_PREVIEW; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.cc b/source/blender/nodes/composite/nodes/node_composite_flip.cc index 37b9a2d020d..aaa2b565ed2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_flip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc @@ -5,9 +5,18 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" +#include "BLI_utildefines.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** Flip ******************** */ @@ -16,7 +25,9 @@ namespace blender::nodes::node_composite_flip_cc { static void cmp_node_flip_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -25,6 +36,56 @@ static void node_composit_buts_flip(uiLayout *layout, bContext *UNUSED(C), Point uiItemR(layout, ptr, "axis", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class FlipOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input("Image"); + Result &result = get_result("Image"); + + /* Can't flip a single value, pass it through to the output. */ + if (input.is_single_value()) { + input.pass_through(result); + return; + } + + GPUShader *shader = shader_manager().get("compositor_flip"); + GPU_shader_bind(shader); + + GPU_shader_uniform_1b( + shader, "flip_x", ELEM(get_flip_mode(), CMP_NODE_FLIP_X, CMP_NODE_FLIP_X_Y)); + GPU_shader_uniform_1b( + shader, "flip_y", ELEM(get_flip_mode(), CMP_NODE_FLIP_Y, CMP_NODE_FLIP_X_Y)); + + input.bind_as_texture(shader, "input_tx"); + + const Domain domain = compute_domain(); + + result.allocate_texture(domain); + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input.unbind_as_texture(); + result.unbind_as_image(); + GPU_shader_unbind(); + } + + CMPNodeFlipMode get_flip_mode() + { + return (CMPNodeFlipMode)bnode().custom1; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new FlipOperation(context, node); +} + } // namespace blender::nodes::node_composite_flip_cc void register_node_type_cmp_flip() @@ -36,6 +97,7 @@ void register_node_type_cmp_flip() cmp_node_type_base(&ntype, CMP_NODE_FLIP, "Flip", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_flip_declare; ntype.draw_buttons = file_ns::node_composit_buts_flip; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.cc b/source/blender/nodes/composite/nodes/node_composite_gamma.cc index b4b8502e915..660d8068231 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.cc +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Gamma Tools ******************** */ @@ -13,15 +17,38 @@ namespace blender::nodes::node_composite_gamma_cc { static void cmp_node_gamma_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_input<decl::Float>(N_("Gamma")) .default_value(1.0f) .min(0.001f) .max(10.0f) - .subtype(PROP_UNSIGNED); + .subtype(PROP_UNSIGNED) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class GammaShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_gamma", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new GammaShaderNode(node); +} + } // namespace blender::nodes::node_composite_gamma_cc void register_node_type_cmp_gamma() @@ -32,6 +59,7 @@ void register_node_type_cmp_gamma() cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_gamma_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.cc b/source/blender/nodes/composite/nodes/node_composite_glare.cc index 7f21d30cfa6..33577d5caf8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_glare.cc +++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_glare_cc { @@ -75,6 +77,23 @@ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), Poin } } +using namespace blender::realtime_compositor; + +class GlareOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new GlareOperation(context, node); +} + } // namespace blender::nodes::node_composite_glare_cc void register_node_type_cmp_glare() @@ -88,6 +107,7 @@ void register_node_type_cmp_glare() ntype.draw_buttons = file_ns::node_composit_buts_glare; node_type_init(&ntype, file_ns::node_composit_init_glare); node_type_storage(&ntype, "NodeGlare", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc index 08a048829df..091864a06f7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc +++ b/source/blender/nodes/composite/nodes/node_composite_hue_sat_val.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Hue Saturation ******************** */ @@ -13,22 +17,56 @@ namespace blender::nodes::node_composite_hue_sat_val_cc { static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Hue")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); b.add_input<decl::Float>(N_("Saturation")) .default_value(1.0f) .min(0.0f) .max(2.0f) - .subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(2); b.add_input<decl::Float>(N_("Value")) .default_value(1.0f) .min(0.0f) .max(2.0f) - .subtype(PROP_FACTOR); - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(3); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(4); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class HueSaturationValueShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_hue_saturation_value", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new HueSaturationValueShaderNode(node); +} + } // namespace blender::nodes::node_composite_hue_sat_val_cc void register_node_type_cmp_hue_sat() @@ -39,6 +77,7 @@ void register_node_type_cmp_hue_sat() cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_huesatval_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc index d252d96f8c3..a84420231aa 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -5,6 +5,12 @@ * \ingroup cmpnodes */ +#include "BKE_colortools.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" #include "BKE_colortools.h" @@ -13,8 +19,15 @@ namespace blender::nodes::node_composite_huecorrect_cc { static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -35,6 +48,53 @@ static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node) cumapping->cur = 1; } +using namespace blender::realtime_compositor; + +class HueCorrectShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + CurveMapping *curve_mapping = get_curve_mapping(); + + 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(material, band_size, band_values, &band_layer); + + 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); + + GPU_stack_link(material, + &bnode(), + "node_composite_hue_correct", + inputs, + outputs, + band_texture, + GPU_constant(&band_layer), + GPU_uniform(range_minimums), + GPU_uniform(range_dividers)); + } + + CurveMapping *get_curve_mapping() + { + return static_cast<CurveMapping *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new HueCorrectShaderNode(node); +} + } // namespace blender::nodes::node_composite_huecorrect_cc void register_node_type_cmp_huecorrect() @@ -48,6 +108,7 @@ void register_node_type_cmp_huecorrect() node_type_size(&ntype, 320, 140, 500); node_type_init(&ntype, file_ns::node_composit_init_huecorrect); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc index 25ab9aa63fc..ac8456cb931 100644 --- a/source/blender/nodes/composite/nodes/node_composite_id_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_id_mask.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** ID Mask ******************** */ @@ -26,6 +28,23 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "use_antialiasing", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class IDMaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("ID value").pass_through(get_result("Alpha")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new IDMaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_id_mask_cc void register_node_type_cmp_idmask() @@ -37,6 +56,7 @@ void register_node_type_cmp_idmask() cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_idmask_declare; ntype.draw_buttons = file_ns::node_composit_buts_id_mask; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_image.cc b/source/blender/nodes/composite/nodes/node_composite_image.cc index d75aa575395..d8852e9333f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.cc +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -8,6 +8,7 @@ #include "node_composite_util.hh" #include "BLI_linklist.h" +#include "BLI_math_vec_types.hh" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -17,6 +18,8 @@ #include "BKE_main.h" #include "BKE_scene.h" +#include "DEG_depsgraph_query.h" + #include "DNA_scene_types.h" #include "RE_engine.h" @@ -27,6 +30,12 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + /* **************** IMAGE (and RenderResult, multilayer image) ******************** */ static bNodeSocketTemplate cmp_node_rlayers_out[] = { @@ -433,6 +442,215 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree), } } +using namespace blender::realtime_compositor; + +class ImageOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + if (!is_valid()) { + allocate_invalid(); + return; + } + + update_image_frame_number(); + + for (const OutputSocketRef *output : node()->outputs()) { + compute_output(output->identifier()); + } + } + + /* Returns true if the node results can be computed, otherwise, returns false. */ + bool is_valid() + { + Image *image = get_image(); + ImageUser *image_user = get_image_user(); + if (!image || !image_user) { + return false; + } + + if (BKE_image_is_multilayer(image)) { + if (!image->rr) { + return false; + } + + RenderLayer *render_layer = get_render_layer(); + if (!render_layer) { + return false; + } + } + + return true; + } + + /* Allocate all needed outputs as invalid. This should be called when is_valid returns false. */ + void allocate_invalid() + { + for (const OutputSocketRef *output : node()->outputs()) { + if (!should_compute_output(output->identifier())) { + continue; + } + + Result &result = get_result(output->identifier()); + result.allocate_invalid(); + } + } + + /* Compute the effective frame number of the image if it was animated and invalidate the cached + * GPU texture if the computed frame number is different. */ + void update_image_frame_number() + { + BKE_image_user_frame_calc(get_image(), get_image_user(), context().get_frame_number()); + } + + void compute_output(StringRef identifier) + { + if (!should_compute_output(identifier)) { + return; + } + + ImageUser image_user = compute_image_user_for_output(identifier); + GPUTexture *image_texture = BKE_image_get_gpu_texture(get_image(), &image_user, nullptr); + + const int2 size = int2(GPU_texture_width(image_texture), GPU_texture_height(image_texture)); + Result &result = get_result(identifier); + result.allocate_texture(Domain(size)); + + GPUShader *shader = shader_manager().get(get_shader_name(identifier)); + GPU_shader_bind(shader); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(image_texture, input_unit); + + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + GPU_shader_unbind(); + GPU_texture_unbind(image_texture); + result.unbind_as_image(); + } + + /* Get a copy of the image user that is appropriate to retrieve the image buffer for the output + * with the given identifier. This essentially sets the appropriate pass and view indices that + * corresponds to the output. */ + ImageUser compute_image_user_for_output(StringRef identifier) + { + ImageUser image_user = *get_image_user(); + + /* Set the needed view. */ + image_user.view = get_view_index(); + + /* Set the needed pass. */ + if (BKE_image_is_multilayer(get_image())) { + image_user.pass = get_pass_index(get_pass_name(identifier)); + BKE_image_multilayer_index(get_image()->rr, &image_user); + } + else { + BKE_image_multiview_index(get_image(), &image_user); + } + + return image_user; + } + + /* Get the shader that should be used to compute the output with the given identifier. The + * shaders just copy the retrieved image textures into the results except for the alpha output, + * which extracts the alpha and writes it to the result instead. Note that a call to a host + * texture copy doesn't work because results are stored in a different half float formats. */ + const char *get_shader_name(StringRef identifier) + { + if (identifier == "Alpha") { + return "compositor_extract_alpha_from_color"; + } + else if (get_result(identifier).type() == ResultType::Color) { + return "compositor_convert_color_to_half_color"; + } + else { + return "compositor_convert_float_to_half_float"; + } + } + + Image *get_image() + { + return (Image *)bnode().id; + } + + ImageUser *get_image_user() + { + return static_cast<ImageUser *>(bnode().storage); + } + + /* Get the render layer selected in the node assuming the image is a multilayer image. */ + RenderLayer *get_render_layer() + { + const ListBase *layers = &get_image()->rr->layers; + return static_cast<RenderLayer *>(BLI_findlink(layers, get_image_user()->layer)); + } + + /* Get the name of the pass corresponding to the output with the given identifier assuming the + * image is a multilayer image. */ + const char *get_pass_name(StringRef identifier) + { + DOutputSocket output = node().output_by_identifier(identifier); + return static_cast<NodeImageLayer *>(output->bsocket()->storage)->pass_name; + } + + /* Get the index of the pass with the given name in the selected render layer's passes list + * assuming the image is a multilayer image. */ + int get_pass_index(const char *name) + { + return BLI_findstringindex(&get_render_layer()->passes, name, offsetof(RenderPass, name)); + } + + /* Get the index of the view selected in the node. If the image is not a multi-view image or only + * has a single view, then zero is returned. Otherwise, if the image is a multi-view image, the + * index of the selected view is returned. However, note that the value of the view member of the + * image user is not the actual index of the view. More specifically, the index 0 is reserved to + * denote the special mode of operation "All", which dynamically selects the view whose name + * matches the view currently being rendered. It follows that the views are then indexed starting + * from 1. So for non zero view values, the actual index of the view is the value of the view + * member of the image user minus 1. */ + int get_view_index() + { + /* The image is not a multi-view image, so just return zero. */ + if (!BKE_image_is_multiview(get_image())) { + return 0; + } + + const ListBase *views = &get_image()->rr->views; + /* There is only one view and its index is 0. */ + if (BLI_listbase_count_at_most(views, 2) < 2) { + return 0; + } + + const int view = get_image_user()->view; + /* The view is not zero, which means it is manually specified and the actual index is then the + * view value minus 1. */ + if (view != 0) { + return view - 1; + } + + /* Otherwise, the view value is zero, denoting the special mode of operation "All", which finds + * the index of the view whose name matches the view currently being rendered. */ + const char *view_name = context().get_view_name().data(); + const int matched_view = BLI_findstringindex(views, view_name, offsetof(RenderView, name)); + + /* No view matches the view currently being rendered, so fallback to the first view. */ + if (matched_view == -1) { + return 0; + } + + return matched_view; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ImageOperation(context, node); +} + } // namespace blender::nodes::node_composite_image_cc void register_node_type_cmp_image() @@ -446,6 +664,7 @@ void register_node_type_cmp_image() node_type_storage( &ntype, "ImageUser", file_ns::node_composit_free_image, file_ns::node_composit_copy_image); node_type_update(&ntype, file_ns::cmp_node_image_update); + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.labelfunc = node_image_label; ntype.flag |= NODE_PREVIEW; @@ -469,7 +688,7 @@ const char *node_cmp_rlayers_sock_to_pass(int sock_index) return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name; } -namespace blender::nodes::node_composite_image_cc { +namespace blender::nodes::node_composite_render_layer_cc { static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr) { @@ -595,11 +814,60 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer RNA_string_set(&op_ptr, "scene", scene_name); } -} // namespace blender::nodes::node_composite_image_cc +using namespace blender::realtime_compositor; + +class RenderLayerOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + const int view_layer = bnode().custom1; + GPUTexture *pass_texture = context().get_input_texture(view_layer, SCE_PASS_COMBINED); + const int2 size = int2(GPU_texture_width(pass_texture), GPU_texture_height(pass_texture)); + + /* Compute image output. */ + Result &image_result = get_result("Image"); + image_result.allocate_texture(Domain(size)); + GPU_texture_copy(image_result.texture(), pass_texture); + + /* Compute alpha output. */ + Result &alpha_result = get_result("Alpha"); + alpha_result.allocate_texture(Domain(size)); + + GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color"); + GPU_shader_bind(shader); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(pass_texture, input_unit); + + alpha_result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + GPU_shader_unbind(); + GPU_texture_unbind(pass_texture); + alpha_result.unbind_as_image(); + + /* Other output passes are not supported for now, so allocate them as invalid. */ + for (const OutputSocketRef *output : node()->outputs()) { + if (output->identifier() != "Image" && output->identifier() != "Alpha") { + get_result(output->identifier()).allocate_invalid(); + } + } + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new RenderLayerOperation(context, node); +} + +} // namespace blender::nodes::node_composite_render_layer_cc void register_node_type_cmp_rlayers() { - namespace file_ns = blender::nodes::node_composite_image_cc; + namespace file_ns = blender::nodes::node_composite_render_layer_cc; static bNodeType ntype; @@ -608,6 +876,7 @@ void register_node_type_cmp_rlayers() ntype.draw_buttons = file_ns::node_composit_buts_viewlayers; ntype.initfunc_api = file_ns::node_composit_init_rlayers; ntype.poll = file_ns::node_composit_poll_rlayers; + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.flag |= NODE_PREVIEW; node_type_storage( &ntype, nullptr, file_ns::node_composit_free_rlayers, file_ns::node_composit_copy_rlayers); diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc index 2958d1b2869..f6e46bef299 100644 --- a/source/blender/nodes/composite/nodes/node_composite_inpaint.cc +++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Inpaint/ ******************** */ @@ -25,6 +27,23 @@ static void node_composit_buts_inpaint(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "distance", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class InpaintOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new InpaintOperation(context, node); +} + } // namespace blender::nodes::node_composite_inpaint_cc void register_node_type_cmp_inpaint() @@ -36,6 +55,7 @@ void register_node_type_cmp_inpaint() cmp_node_type_base(&ntype, CMP_NODE_INPAINT, "Inpaint", NODE_CLASS_OP_FILTER); ntype.declare = file_ns::cmp_node_inpaint_declare; ntype.draw_buttons = file_ns::node_composit_buts_inpaint; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.cc b/source/blender/nodes/composite/nodes/node_composite_invert.cc index 6dff043537a..4bfcc7b6b9c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_invert.cc +++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** INVERT ******************** */ @@ -16,8 +20,15 @@ namespace blender::nodes::node_composite_invert_cc { static void cmp_node_invert_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_input<decl::Color>(N_("Color")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Color")); } @@ -35,6 +46,45 @@ static void node_composit_buts_invert(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(col, ptr, "invert_alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class InvertShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float do_rgb = get_do_rgb(); + const float do_alpha = get_do_alpha(); + + GPU_stack_link(material, + &bnode(), + "node_composite_invert", + inputs, + outputs, + GPU_constant(&do_rgb), + GPU_constant(&do_alpha)); + } + + bool get_do_rgb() + { + return bnode().custom1 & CMP_CHAN_RGB; + } + + bool get_do_alpha() + { + return bnode().custom1 & CMP_CHAN_A; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new InvertShaderNode(node); +} + } // namespace blender::nodes::node_composite_invert_cc void register_node_type_cmp_invert() @@ -47,6 +97,7 @@ void register_node_type_cmp_invert() ntype.declare = file_ns::cmp_node_invert_declare; ntype.draw_buttons = file_ns::node_composit_buts_invert; node_type_init(&ntype, file_ns::node_composit_init_invert); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.cc b/source/blender/nodes/composite/nodes/node_composite_keying.cc index fbfdf2ad3c6..8b584e216cd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keying.cc +++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc @@ -14,6 +14,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Keying ******************** */ @@ -63,6 +65,25 @@ static void node_composit_buts_keying(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "blur_post", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class KeyingOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Matte").allocate_invalid(); + get_result("Edges").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new KeyingOperation(context, node); +} + } // namespace blender::nodes::node_composite_keying_cc void register_node_type_cmp_keying() @@ -77,6 +98,7 @@ void register_node_type_cmp_keying() node_type_init(&ntype, file_ns::node_composit_init_keying); node_type_storage( &ntype, "NodeKeyingData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc index e835ee9e721..9eec705b6ca 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc +++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc @@ -21,6 +21,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Keying Screen ******************** */ @@ -78,6 +80,23 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point } } +using namespace blender::realtime_compositor; + +class KeyingScreenOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Screen").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new KeyingScreenOperation(context, node); +} + } // namespace blender::nodes::node_composite_keyingscreen_cc void register_node_type_cmp_keyingscreen() @@ -92,6 +111,7 @@ void register_node_type_cmp_keyingscreen() ntype.initfunc_api = file_ns::node_composit_init_keyingscreen; node_type_storage( &ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc index 593b7cc9b71..2d4c0afcda7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lensdist.cc +++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc @@ -5,20 +5,48 @@ * \ingroup cmpnodes */ +#include "BLI_math_base.h" +#include "BLI_math_vec_types.hh" + #include "RNA_access.h" #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" +/* Distortion can't be exactly -1.0 as it will cause infinite pincushion distortion. */ +#define MINIMUM_DISTORTION -0.999f +/* Arbitrary scaling factor for the dispersion input in projector distortion mode. */ +#define PROJECTOR_DISPERSION_SCALE 5.0f +/* Arbitrary scaling factor for the dispersion input in screen distortion mode. */ +#define SCREEN_DISPERSION_SCALE 4.0f +/* Arbitrary scaling factor for the distortion input. */ +#define DISTORTION_SCALE 4.0f + namespace blender::nodes::node_composite_lensdist_cc { static void cmp_node_lensdist_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Distort")).default_value(0.0f).min(-0.999f).max(1.0f); - b.add_input<decl::Float>(N_("Dispersion")).default_value(0.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Distort")) + .default_value(0.0f) + .min(MINIMUM_DISTORTION) + .max(1.0f) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Dispersion")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } @@ -42,6 +70,178 @@ static void node_composit_buts_lensdist(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "use_fit", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class LensDistortionOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + if (is_identity()) { + get_input("Image").pass_through(get_result("Image")); + return; + } + + if (get_is_projector()) { + execute_projector_distortion(); + } + else { + execute_screen_distortion(); + } + } + + void execute_projector_distortion() + { + GPUShader *shader = shader_manager().get("compositor_projector_lens_distortion"); + GPU_shader_bind(shader); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + GPU_texture_filter_mode(input_image.texture(), true); + GPU_texture_wrap_mode(input_image.texture(), false, false); + + const Domain domain = compute_domain(); + + const float dispersion = (get_dispersion() * PROJECTOR_DISPERSION_SCALE) / domain.size.x; + GPU_shader_uniform_1f(shader, "dispersion", dispersion); + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + void execute_screen_distortion() + { + GPUShader *shader = shader_manager().get(get_screen_distortion_shader()); + GPU_shader_bind(shader); + + const Result &input_image = get_input("Image"); + input_image.bind_as_texture(shader, "input_tx"); + + GPU_texture_filter_mode(input_image.texture(), true); + GPU_texture_wrap_mode(input_image.texture(), false, false); + + const Domain domain = compute_domain(); + + const float3 chromatic_distortion = compute_chromatic_distortion(); + GPU_shader_uniform_3fv(shader, "chromatic_distortion", chromatic_distortion); + + GPU_shader_uniform_1f(shader, "scale", compute_scale()); + + Result &output_image = get_result("Image"); + output_image.allocate_texture(domain); + output_image.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, domain.size); + + input_image.unbind_as_texture(); + output_image.unbind_as_image(); + GPU_shader_unbind(); + } + + const char *get_screen_distortion_shader() + { + if (get_is_jitter()) { + return "compositor_screen_lens_distortion_jitter"; + } + return "compositor_screen_lens_distortion"; + } + + float get_distortion() + { + const Result &input = get_input("Distort"); + return clamp_f(input.get_float_value_default(0.0f), MINIMUM_DISTORTION, 1.0f); + } + + float get_dispersion() + { + const Result &input = get_input("Dispersion"); + return clamp_f(input.get_float_value_default(0.0f), 0.0f, 1.0f); + } + + /* Get the distortion amount for each channel. The green channel has a distortion amount that + * matches that specified in the node inputs, while the red and blue channels have higher and + * lower distortion amounts respectively based on the dispersion value. */ + float3 compute_chromatic_distortion() + { + const float green_distortion = get_distortion(); + const float dispersion = get_dispersion() / SCREEN_DISPERSION_SCALE; + const float red_distortion = clamp_f(green_distortion + dispersion, MINIMUM_DISTORTION, 1.0f); + const float blue_distortion = clamp_f(green_distortion - dispersion, MINIMUM_DISTORTION, 1.0f); + return float3(red_distortion, green_distortion, blue_distortion) * DISTORTION_SCALE; + } + + /* The distortion model will distort the image in such a way that the result will no longer + * fit the domain of the original image, so we scale the image to account for that. If get_is_fit + * is false, then the scaling factor will be such that the furthest pixels horizontally and + * vertically are at the boundary of the image. Otherwise, if get_is_fit is true, the scaling + * factor will be such that the furthest pixels diagonally are at the corner of the image. */ + float compute_scale() + { + const float3 distortion = compute_chromatic_distortion() / DISTORTION_SCALE; + const float maximum_distortion = max_fff(distortion[0], distortion[1], distortion[2]); + + if (get_is_fit() && (maximum_distortion > 0.0f)) { + return 1.0f / (1.0f + 2.0f * maximum_distortion); + } + return 1.0f / (1.0f + maximum_distortion); + } + + bool get_is_projector() + { + return get_node_lens_distortion().proj; + } + + bool get_is_jitter() + { + return get_node_lens_distortion().jit; + } + + bool get_is_fit() + { + return get_node_lens_distortion().fit; + } + + NodeLensDist &get_node_lens_distortion() + { + return *static_cast<NodeLensDist *>(bnode().storage); + } + + /* Returns true if the operation does nothing and the input can be passed through. */ + bool is_identity() + { + /* The input is a single value and the operation does nothing. */ + if (get_input("Image").is_single_value()) { + return true; + } + + /* Projector have zero dispersion and does nothing. */ + if (get_is_projector() && get_dispersion() == 0.0f) { + return true; + } + + /* Both distortion and dispersion are zero and the operation does nothing. */ + if (get_distortion() == 0.0f && get_dispersion() == 0.0f) { + return true; + } + + return false; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new LensDistortionOperation(context, node); +} + } // namespace blender::nodes::node_composite_lensdist_cc void register_node_type_cmp_lensdist() @@ -56,6 +256,7 @@ void register_node_type_cmp_lensdist() node_type_init(&ntype, file_ns::node_composit_init_lensdist); node_type_storage( &ntype, "NodeLensDist", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.cc b/source/blender/nodes/composite/nodes/node_composite_levels.cc index a30567672f0..2f1ebeb79b5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.cc +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** LEVELS ******************** */ @@ -31,6 +33,24 @@ static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C) uiItemR(layout, ptr, "channel", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class LevelsOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Mean").allocate_invalid(); + get_result("Std Dev").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new LevelsOperation(context, node); +} + } // namespace blender::nodes::node_composite_levels_cc void register_node_type_cmp_view_levels() @@ -44,6 +64,7 @@ void register_node_type_cmp_view_levels() ntype.draw_buttons = file_ns::node_composit_buts_view_levels; ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_view_levels); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc index 94697a2aafd..092a12a7ea4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_luma_matte.cc @@ -5,9 +5,15 @@ * \ingroup cmpnodes */ +#include "IMB_colormanagement.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* ******************* Luma Matte Node ********************************* */ @@ -16,7 +22,9 @@ namespace blender::nodes::node_composite_luma_matte_cc { static void cmp_node_luma_matte_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); b.add_output<decl::Float>(N_("Matte")); } @@ -40,6 +48,53 @@ static void node_composit_buts_luma_matte(uiLayout *layout, bContext *UNUSED(C), col, ptr, "limit_min", UI_ITEM_R_SPLIT_EMPTY_NAME | UI_ITEM_R_SLIDER, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class LuminanceMatteShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float high = get_high(); + const float low = get_low(); + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + + GPU_stack_link(material, + &bnode(), + "node_composite_luminance_matte", + inputs, + outputs, + GPU_uniform(&high), + GPU_uniform(&low), + GPU_constant(luminance_coefficients)); + } + + NodeChroma *get_node_chroma() + { + return static_cast<NodeChroma *>(bnode().storage); + } + + float get_high() + { + return get_node_chroma()->t1; + } + + float get_low() + { + return get_node_chroma()->t2; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new LuminanceMatteShaderNode(node); +} + } // namespace blender::nodes::node_composite_luma_matte_cc void register_node_type_cmp_luma_matte() @@ -54,6 +109,7 @@ void register_node_type_cmp_luma_matte() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_luma_matte); node_type_storage(&ntype, "NodeChroma", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_map_range.cc b/source/blender/nodes/composite/nodes/node_composite_map_range.cc index e52c6d096b9..e72869efa93 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_range.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_range.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Map Range ******************** */ @@ -16,11 +20,31 @@ namespace blender::nodes::node_composite_map_range_cc { static void cmp_node_map_range_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("From Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("From Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("To Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("To Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Value")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("From Min")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(1); + b.add_input<decl::Float>(N_("From Max")) + .default_value(1.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(2); + b.add_input<decl::Float>(N_("To Min")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(3); + b.add_input<decl::Float>(N_("To Max")) + .default_value(1.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(4); b.add_output<decl::Float>(N_("Value")); } @@ -32,6 +56,38 @@ static void node_composit_buts_map_range(uiLayout *layout, bContext *UNUSED(C), uiItemR(col, ptr, "use_clamp", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class MapRangeShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const float should_clamp = get_should_clamp(); + + GPU_stack_link(material, + &bnode(), + "node_composite_map_range", + inputs, + outputs, + GPU_constant(&should_clamp)); + } + + bool get_should_clamp() + { + return bnode().custom1; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new MapRangeShaderNode(node); +} + } // namespace blender::nodes::node_composite_map_range_cc void register_node_type_cmp_map_range() @@ -43,6 +99,7 @@ void register_node_type_cmp_map_range() cmp_node_type_base(&ntype, CMP_NODE_MAP_RANGE, "Map Range", NODE_CLASS_OP_VECTOR); ntype.declare = file_ns::cmp_node_map_range_declare; ntype.draw_buttons = file_ns::node_composit_buts_map_range; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc index 31961f07ea4..4f660d62c3b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_uv.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_uv.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Map UV ******************** */ @@ -26,6 +28,23 @@ static void node_composit_buts_map_uv(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "alpha", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class MapUVOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new MapUVOperation(context, node); +} + } // namespace blender::nodes::node_composite_map_uv_cc void register_node_type_cmp_mapuv() @@ -37,6 +56,7 @@ void register_node_type_cmp_mapuv() cmp_node_type_base(&ntype, CMP_NODE_MAP_UV, "Map UV", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_map_uv_declare; ntype.draw_buttons = file_ns::node_composit_buts_map_uv; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_map_value.cc b/source/blender/nodes/composite/nodes/node_composite_map_value.cc index bb42628ed3d..ec9b2d56636 100644 --- a/source/blender/nodes/composite/nodes/node_composite_map_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_map_value.cc @@ -12,6 +12,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** MAP VALUE ******************** */ @@ -20,7 +24,11 @@ namespace blender::nodes::node_composite_map_value_cc { static void cmp_node_map_value_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Value")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Value")); } @@ -50,6 +58,56 @@ static void node_composit_buts_map_value(uiLayout *layout, bContext *UNUSED(C), uiItemR(sub, ptr, "max", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class MapValueShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + const TexMapping *texture_mapping = get_texture_mapping(); + + const float use_min = get_use_min(); + const float use_max = get_use_max(); + + GPU_stack_link(material, + &bnode(), + "node_composite_map_value", + inputs, + outputs, + GPU_uniform(texture_mapping->loc), + GPU_uniform(texture_mapping->size), + GPU_constant(&use_min), + GPU_uniform(texture_mapping->min), + GPU_constant(&use_max), + GPU_uniform(texture_mapping->max)); + } + + TexMapping *get_texture_mapping() + { + return static_cast<TexMapping *>(bnode().storage); + } + + bool get_use_min() + { + return get_texture_mapping()->flag & TEXMAP_CLIP_MIN; + } + + bool get_use_max() + { + return get_texture_mapping()->flag & TEXMAP_CLIP_MAX; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new MapValueShaderNode(node); +} + } // namespace blender::nodes::node_composite_map_value_cc void register_node_type_cmp_map_value() @@ -63,6 +121,7 @@ void register_node_type_cmp_map_value() ntype.draw_buttons = file_ns::node_composit_buts_map_value; node_type_init(&ntype, file_ns::node_composit_init_map_value); node_type_storage(&ntype, "TexMapping", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.cc b/source/blender/nodes/composite/nodes/node_composite_mask.cc index 5b8fac5d1c0..2372dbae3f2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Mask ******************** */ @@ -74,6 +76,23 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p } } +using namespace blender::realtime_compositor; + +class MaskOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Mask").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new MaskOperation(context, node); +} + } // namespace blender::nodes::node_composite_mask_cc void register_node_type_cmp_mask() @@ -87,6 +106,7 @@ void register_node_type_cmp_mask() ntype.draw_buttons = file_ns::node_composit_buts_mask; node_type_init(&ntype, file_ns::node_composit_init_mask); ntype.labelfunc = file_ns::node_mask_label; + ntype.get_compositor_operation = file_ns::get_compositor_operation; node_type_storage(&ntype, "NodeMask", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_math.cc b/source/blender/nodes/composite/nodes/node_composite_math.cc index 7b2eadef2cb..4baf057913e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.cc +++ b/source/blender/nodes/composite/nodes/node_composite_math.cc @@ -5,6 +5,12 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + +#include "NOD_math_functions.hh" + #include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ @@ -13,18 +19,72 @@ namespace blender::nodes::node_composite_math_cc { static void cmp_node_math_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Float>(N_("Value")) + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f) + .compositor_domain_priority(0); b.add_input<decl::Float>(N_("Value"), "Value_001") .default_value(0.5f) .min(-10000.0f) - .max(10000.0f); + .max(10000.0f) + .compositor_domain_priority(1); b.add_input<decl::Float>(N_("Value"), "Value_002") .default_value(0.5f) .min(-10000.0f) - .max(10000.0f); + .max(10000.0f) + .compositor_domain_priority(2); b.add_output<decl::Float>(N_("Value")); } +using namespace blender::realtime_compositor; + +class MathShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + + if (!get_should_clamp()) { + return; + } + + const float min = 0.0f; + const float max = 1.0f; + GPU_link(material, + "clamp_value", + get_output("Value").link, + GPU_constant(&min), + GPU_constant(&max), + &get_output("Value").link); + } + + NodeMathOperation get_operation() + { + return (NodeMathOperation)bnode().custom1; + } + + const char *get_shader_function_name() + { + return get_float_math_operation_info(get_operation())->shader_name.c_str(); + } + + bool get_should_clamp() + { + return bnode().custom2 & SHD_MATH_CLAMP; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new MathShaderNode(node); +} + } // namespace blender::nodes::node_composite_math_cc void register_node_type_cmp_math() @@ -37,6 +97,7 @@ void register_node_type_cmp_math() ntype.declare = file_ns::cmp_node_math_declare; ntype.labelfunc = node_math_label; node_type_update(&ntype, node_math_update); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc index fc11aa188b0..a1fbbfe7d40 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc @@ -5,6 +5,14 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" + +#include "DNA_material_types.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** MIX RGB ******************** */ @@ -13,12 +21,122 @@ namespace blender::nodes::node_composite_mixrgb_cc { static void cmp_node_mixrgb_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Color>(N_("Image"), "Image_001").default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Float>(N_("Fac")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(2); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Color>(N_("Image"), "Image_001") + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class MixRGBShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + if (get_use_alpha()) { + GPU_link(material, + "multiply_by_alpha", + get_input_link("Fac"), + get_input_link("Image_001"), + &get_input("Fac").link); + } + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + + if (!get_should_clamp()) { + return; + } + + const float min[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float max[4] = {1.0f, 1.0f, 1.0f, 1.0f}; + GPU_link(material, + "clamp_color", + get_output("Image").link, + GPU_constant(min), + GPU_constant(max), + &get_output("Image").link); + } + + int get_mode() + { + return bnode().custom1; + } + + const char *get_shader_function_name() + { + switch (get_mode()) { + case MA_RAMP_BLEND: + return "mix_blend"; + case MA_RAMP_ADD: + return "mix_add"; + case MA_RAMP_MULT: + return "mix_mult"; + case MA_RAMP_SUB: + return "mix_sub"; + case MA_RAMP_SCREEN: + return "mix_screen"; + case MA_RAMP_DIV: + return "mix_div"; + case MA_RAMP_DIFF: + return "mix_diff"; + case MA_RAMP_DARK: + return "mix_dark"; + case MA_RAMP_LIGHT: + return "mix_light"; + case MA_RAMP_OVERLAY: + return "mix_overlay"; + case MA_RAMP_DODGE: + return "mix_dodge"; + case MA_RAMP_BURN: + return "mix_burn"; + case MA_RAMP_HUE: + return "mix_hue"; + case MA_RAMP_SAT: + return "mix_sat"; + case MA_RAMP_VAL: + return "mix_val"; + case MA_RAMP_COLOR: + return "mix_color"; + case MA_RAMP_SOFT: + return "mix_soft"; + case MA_RAMP_LINEAR: + return "mix_linear"; + } + + BLI_assert_unreachable(); + return nullptr; + } + + bool get_use_alpha() + { + return bnode().custom2 & SHD_MIXRGB_USE_ALPHA; + } + + bool get_should_clamp() + { + return bnode().custom2 & SHD_MIXRGB_CLAMP; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new MixRGBShaderNode(node); +} + } // namespace blender::nodes::node_composite_mixrgb_cc void register_node_type_cmp_mix_rgb() @@ -31,6 +149,7 @@ void register_node_type_cmp_mix_rgb() ntype.flag |= NODE_PREVIEW; ntype.declare = file_ns::cmp_node_mixrgb_declare; ntype.labelfunc = node_blend_label; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc index a4d5f294fe0..ec95de3da18 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.cc +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -5,8 +5,13 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + #include "BKE_context.h" #include "BKE_lib_id.h" +#include "BKE_movieclip.h" +#include "BKE_tracking.h" + #include "DNA_defaults.h" #include "RNA_access.h" @@ -14,6 +19,12 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_movieclip_cc { @@ -79,6 +90,177 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point uiTemplateColorspaceSettings(layout, &clipptr, "colorspace_settings"); } +using namespace blender::realtime_compositor; + +class MovieClipOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUTexture *movie_clip_texture = get_movie_clip_texture(); + + compute_image(movie_clip_texture); + compute_alpha(movie_clip_texture); + compute_stabilization_data(movie_clip_texture); + + free_movie_clip_texture(); + } + + void compute_image(GPUTexture *movie_clip_texture) + { + if (!should_compute_output("Image")) { + return; + } + + Result &result = get_result("Image"); + + /* The movie clip texture is invalid or missing, set an appropriate fallback value. */ + if (!movie_clip_texture) { + result.allocate_invalid(); + return; + } + + const int2 size = int2(GPU_texture_width(movie_clip_texture), + GPU_texture_height(movie_clip_texture)); + result.allocate_texture(Domain(size)); + + GPUShader *shader = shader_manager().get("compositor_convert_color_to_half_color"); + GPU_shader_bind(shader); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(movie_clip_texture, input_unit); + + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + GPU_shader_unbind(); + GPU_texture_unbind(movie_clip_texture); + result.unbind_as_image(); + } + + void compute_alpha(GPUTexture *movie_clip_texture) + { + if (!should_compute_output("Alpha")) { + return; + } + + Result &result = get_result("Alpha"); + + /* The movie clip texture is invalid or missing, set an appropriate fallback value. */ + if (!movie_clip_texture) { + result.allocate_single_value(); + result.set_float_value(1.0f); + return; + } + + const int2 size = int2(GPU_texture_width(movie_clip_texture), + GPU_texture_height(movie_clip_texture)); + result.allocate_texture(Domain(size)); + + GPUShader *shader = shader_manager().get("compositor_extract_alpha_from_color"); + GPU_shader_bind(shader); + + const int input_unit = GPU_shader_get_texture_binding(shader, "input_tx"); + GPU_texture_bind(movie_clip_texture, input_unit); + + result.bind_as_image(shader, "output_img"); + + compute_dispatch_threads_at_least(shader, size); + + GPU_shader_unbind(); + GPU_texture_unbind(movie_clip_texture); + result.unbind_as_image(); + } + + void compute_stabilization_data(GPUTexture *movie_clip_texture) + { + /* The movie clip texture is invalid or missing, set appropriate fallback values. */ + if (!movie_clip_texture) { + if (should_compute_output("Offset X")) { + Result &result = get_result("Offset X"); + result.allocate_single_value(); + result.set_float_value(0.0f); + } + if (should_compute_output("Offset Y")) { + Result &result = get_result("Offset Y"); + result.allocate_single_value(); + result.set_float_value(0.0f); + } + if (should_compute_output("Scale")) { + Result &result = get_result("Scale"); + result.allocate_single_value(); + result.set_float_value(1.0f); + } + if (should_compute_output("Angle")) { + Result &result = get_result("Angle"); + result.allocate_single_value(); + result.set_float_value(0.0f); + } + return; + } + + MovieClip *movie_clip = get_movie_clip(); + const int frame_number = BKE_movieclip_remap_scene_to_clip_frame(movie_clip, + context().get_frame_number()); + const int width = GPU_texture_width(movie_clip_texture); + const int height = GPU_texture_height(movie_clip_texture); + + /* If the movie clip has no stabilization data, it will initialize the given values with + * fallback values regardless, so no need to handle that case. */ + float2 offset; + float scale, angle; + BKE_tracking_stabilization_data_get( + movie_clip, frame_number, width, height, offset, &scale, &angle); + + if (should_compute_output("Offset X")) { + Result &result = get_result("Offset X"); + result.allocate_single_value(); + result.set_float_value(offset.x); + } + if (should_compute_output("Offset Y")) { + Result &result = get_result("Offset Y"); + result.allocate_single_value(); + result.set_float_value(offset.y); + } + if (should_compute_output("Scale")) { + Result &result = get_result("Scale"); + result.allocate_single_value(); + result.set_float_value(scale); + } + if (should_compute_output("Angle")) { + Result &result = get_result("Angle"); + result.allocate_single_value(); + result.set_float_value(angle); + } + } + + GPUTexture *get_movie_clip_texture() + { + MovieClip *movie_clip = get_movie_clip(); + MovieClipUser *movie_clip_user = static_cast<MovieClipUser *>(bnode().storage); + BKE_movieclip_user_set_frame(movie_clip_user, context().get_frame_number()); + return BKE_movieclip_get_gpu_texture(movie_clip, movie_clip_user); + } + + void free_movie_clip_texture() + { + MovieClip *movie_clip = get_movie_clip(); + return BKE_movieclip_free_gputexture(movie_clip); + } + + MovieClip *get_movie_clip() + { + return (MovieClip *)bnode().id; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new MovieClipOperation(context, node); +} + } // namespace blender::nodes::node_composite_movieclip_cc void register_node_type_cmp_movieclip() @@ -91,6 +273,7 @@ void register_node_type_cmp_movieclip() ntype.declare = file_ns::cmp_node_movieclip_declare; ntype.draw_buttons = file_ns::node_composit_buts_movieclip; ntype.draw_buttons_ex = file_ns::node_composit_buts_movieclip_ex; + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.initfunc_api = file_ns::init; ntype.flag |= NODE_PREVIEW; node_type_storage( diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc index 4d52a767b8a..88638586594 100644 --- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc +++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc @@ -12,6 +12,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -81,6 +83,23 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po uiItemR(layout, ptr, "distortion_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class MovieDistortionOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new MovieDistortionOperation(context, node); +} + } // namespace blender::nodes::node_composite_moviedistortion_cc void register_node_type_cmp_moviedistortion() @@ -95,6 +114,7 @@ void register_node_type_cmp_moviedistortion() ntype.labelfunc = file_ns::label; ntype.initfunc_api = file_ns::init; node_type_storage(&ntype, nullptr, file_ns::storage_free, file_ns::storage_copy); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.cc b/source/blender/nodes/composite/nodes/node_composite_normal.cc index b4dd0bbacd0..f61ace01cfd 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normal.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** NORMAL ******************** */ @@ -17,7 +21,8 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b) .default_value({0.0f, 0.0f, 1.0f}) .min(-1.0f) .max(1.0f) - .subtype(PROP_DIRECTION); + .subtype(PROP_DIRECTION) + .compositor_domain_priority(0); b.add_output<decl::Vector>(N_("Normal")) .default_value({0.0f, 0.0f, 1.0f}) .min(-1.0f) @@ -26,6 +31,37 @@ static void cmp_node_normal_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Dot")); } +using namespace blender::realtime_compositor; + +class NormalShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, + &bnode(), + "node_composite_normal", + inputs, + outputs, + GPU_uniform(get_vector_value())); + } + + /* The vector value is stored in the default value of the output socket. */ + float *get_vector_value() + { + return node().output_by_identifier("Normal")->default_value<bNodeSocketValueVector>()->value; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new NormalShaderNode(node); +} + } // namespace blender::nodes::node_composite_normal_cc void register_node_type_cmp_normal() @@ -36,6 +72,7 @@ void register_node_type_cmp_normal() cmp_node_type_base(&ntype, CMP_NODE_NORMAL, "Normal", NODE_CLASS_OP_VECTOR); ntype.declare = file_ns::cmp_node_normal_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.cc b/source/blender/nodes/composite/nodes/node_composite_normalize.cc index 49318279bdb..21765825468 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normalize.cc +++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** NORMALIZE single channel, useful for Z buffer ******************** */ @@ -17,6 +19,23 @@ static void cmp_node_normalize_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Value")); } +using namespace blender::realtime_compositor; + +class NormalizeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Value").pass_through(get_result("Value")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new NormalizeOperation(context, node); +} + } // namespace blender::nodes::node_composite_normalize_cc void register_node_type_cmp_normalize() @@ -27,6 +46,7 @@ void register_node_type_cmp_normalize() cmp_node_type_base(&ntype, CMP_NODE_NORMALIZE, "Normalize", NODE_CLASS_OP_VECTOR); ntype.declare = file_ns::cmp_node_normalize_declare; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_output_file.cc b/source/blender/nodes/composite/nodes/node_composite_output_file.cc index 84235b085a4..5ed383977a5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_output_file.cc +++ b/source/blender/nodes/composite/nodes/node_composite_output_file.cc @@ -24,6 +24,8 @@ #include "IMB_openexr.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** OUTPUT FILE ******************** */ @@ -439,6 +441,22 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi } } +using namespace blender::realtime_compositor; + +class OutputFileOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new OutputFileOperation(context, node); +} + } // namespace blender::nodes::node_composite_output_file_cc void register_node_type_cmp_output_file() @@ -455,6 +473,7 @@ void register_node_type_cmp_output_file() node_type_storage( &ntype, "NodeImageMultiFile", file_ns::free_output_file, file_ns::copy_output_file); node_type_update(&ntype, file_ns::update_output_file); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc index 529aa0f84de..4567464a547 100644 --- a/source/blender/nodes/composite/nodes/node_composite_pixelate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Pixelate ******************** */ @@ -17,6 +19,23 @@ static void cmp_node_pixelate_declare(NodeDeclarationBuilder &b) b.add_output<decl::Color>(N_("Color")); } +using namespace blender::realtime_compositor; + +class PixelateOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Color").pass_through(get_result("Color")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new PixelateOperation(context, node); +} + } // namespace blender::nodes::node_composite_pixelate_cc void register_node_type_cmp_pixelate() @@ -27,6 +46,7 @@ void register_node_type_cmp_pixelate() cmp_node_type_base(&ntype, CMP_NODE_PIXELATE, "Pixelate", NODE_CLASS_OP_FILTER); ntype.declare = file_ns::cmp_node_pixelate_declare; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc index 6557478fc4b..68dc020a02e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc +++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc @@ -18,6 +18,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_planetrackdeform_cc { @@ -107,6 +109,24 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P } } +using namespace blender::realtime_compositor; + +class PlaneTrackDeformOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Plane").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new PlaneTrackDeformOperation(context, node); +} + } // namespace blender::nodes::node_composite_planetrackdeform_cc void register_node_type_cmp_planetrackdeform() @@ -121,6 +141,7 @@ void register_node_type_cmp_planetrackdeform() ntype.initfunc_api = file_ns::init; node_type_storage( &ntype, "NodePlaneTrackDeformData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.cc b/source/blender/nodes/composite/nodes/node_composite_posterize.cc index c97035d55ea..1268219e7e2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_posterize.cc +++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc @@ -5,6 +5,10 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Posterize ******************** */ @@ -13,11 +17,37 @@ namespace blender::nodes::node_composite_posterize_cc { static void cmp_node_posterize_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Steps")).default_value(8.0f).min(2.0f).max(1024.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Steps")) + .default_value(8.0f) + .min(2.0f) + .max(1024.0f) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } +using namespace blender::realtime_compositor; + +class PosterizeShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_posterize", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new PosterizeShaderNode(node); +} + } // namespace blender::nodes::node_composite_posterize_cc void register_node_type_cmp_posterize() @@ -28,6 +58,7 @@ void register_node_type_cmp_posterize() cmp_node_type_base(&ntype, CMP_NODE_POSTERIZE, "Posterize", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_posterize_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc index 000cc9df90a..c814ea5f738 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.cc +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** Premul and Key Alpha Convert ******************** */ @@ -16,7 +20,9 @@ namespace blender::nodes::node_composite_premulkey_cc { static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Color>(N_("Image")); } @@ -25,6 +31,36 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "mapping", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class AlphaConvertShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + if (get_mode() == 0) { + GPU_stack_link(material, &bnode(), "color_alpha_premultiply", inputs, outputs); + return; + } + + GPU_stack_link(material, &bnode(), "color_alpha_unpremultiply", inputs, outputs); + } + + CMPNodeAlphaConvertMode get_mode() + { + return (CMPNodeAlphaConvertMode)bnode().custom1; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new AlphaConvertShaderNode(node); +} + } // namespace blender::nodes::node_composite_premulkey_cc void register_node_type_cmp_premulkey() @@ -36,6 +72,7 @@ void register_node_type_cmp_premulkey() cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_premulkey_declare; ntype.draw_buttons = file_ns::node_composit_buts_premulkey; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc index 5bc4c67dd8e..6f3a00af7e3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -5,6 +5,12 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + +#include "DNA_node_types.h" + +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** RGB ******************** */ @@ -16,6 +22,29 @@ static void cmp_node_rgb_declare(NodeDeclarationBuilder &b) b.add_output<decl::Color>(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); } +using namespace blender::realtime_compositor; + +class RGBOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &result = get_result("RGBA"); + result.allocate_single_value(); + + const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first); + float4 color = float4(static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value); + + result.set_color_value(color); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new RGBOperation(context, node); +} + } // namespace blender::nodes::node_composite_rgb_cc void register_node_type_cmp_rgb() @@ -27,6 +56,7 @@ void register_node_type_cmp_rgb() cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT); ntype.declare = file_ns::cmp_node_rgb_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.cc b/source/blender/nodes/composite/nodes/node_composite_rotate.cc index a083bc1837b..35caa3cd242 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rotate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc @@ -5,9 +5,14 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" +#include "BLI_float3x3.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Rotate ******************** */ @@ -16,12 +21,15 @@ namespace blender::nodes::node_composite_rotate_cc { static void cmp_node_rotate_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_input<decl::Float>(N_("Degr")) .default_value(0.0f) .min(-10000.0f) .max(10000.0f) - .subtype(PROP_ANGLE); + .subtype(PROP_ANGLE) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } @@ -35,6 +43,47 @@ static void node_composit_buts_rotate(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class RotateOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input("Image"); + Result &result = get_result("Image"); + input.pass_through(result); + + const float rotation = get_input("Degr").get_float_value_default(0.0f); + + const float3x3 transformation = float3x3::from_rotation(rotation); + + result.transform(transformation); + result.get_realization_options().interpolation = get_interpolation(); + } + + Interpolation get_interpolation() + { + switch (bnode().custom1) { + case 0: + return Interpolation::Nearest; + case 1: + return Interpolation::Bilinear; + case 2: + return Interpolation::Bicubic; + } + + BLI_assert_unreachable(); + return Interpolation::Nearest; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new RotateOperation(context, node); +} + } // namespace blender::nodes::node_composite_rotate_cc void register_node_type_cmp_rotate() @@ -47,6 +96,7 @@ void register_node_type_cmp_rotate() ntype.declare = file_ns::cmp_node_rotate_declare; ntype.draw_buttons = file_ns::node_composit_buts_rotate; node_type_init(&ntype, file_ns::node_composit_init_rotate); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.cc b/source/blender/nodes/composite/nodes/node_composite_scale.cc index b2b42a3613c..8b43ae8c9ca 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Scale ******************** */ @@ -55,6 +57,23 @@ static void node_composit_buts_scale(uiLayout *layout, bContext *UNUSED(C), Poin } } +using namespace blender::realtime_compositor; + +class ScaleOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ScaleOperation(context, node); +} + } // namespace blender::nodes::node_composite_scale_cc void register_node_type_cmp_scale() @@ -67,6 +86,7 @@ void register_node_type_cmp_scale() ntype.declare = file_ns::cmp_node_scale_declare; ntype.draw_buttons = file_ns::node_composit_buts_scale; node_type_update(&ntype, file_ns::node_composite_update_scale); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc index 20bafb0d3d4..1f5317378bb 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scene_time.cc +++ b/source/blender/nodes/composite/nodes/node_composite_scene_time.cc @@ -3,6 +3,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes { @@ -13,6 +15,38 @@ static void cmp_node_scene_time_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Frame")); } +using namespace blender::realtime_compositor; + +class SceneTimeOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + execute_seconds(); + execute_frame(); + } + + void execute_seconds() + { + Result &result = get_result("Seconds"); + result.allocate_single_value(); + result.set_float_value(context().get_time()); + } + + void execute_frame() + { + Result &result = get_result("Frame"); + result.allocate_single_value(); + result.set_float_value(static_cast<float>(context().get_frame_number())); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new SceneTimeOperation(context, node); +} + } // namespace blender::nodes void register_node_type_cmp_scene_time() @@ -21,5 +55,7 @@ void register_node_type_cmp_scene_time() cmp_node_type_base(&ntype, CMP_NODE_SCENE_TIME, "Scene Time", NODE_CLASS_INPUT); ntype.declare = blender::nodes::cmp_node_scene_time_declare; + ntype.get_compositor_operation = blender::nodes::get_compositor_operation; + nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc index b253656a628..d1f0b7977f8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_color.cc @@ -1,5 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "BLI_assert.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" static void node_cmp_combsep_color_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -58,7 +64,9 @@ 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_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Red")); b.add_output<decl::Float>(N_("Green")); b.add_output<decl::Float>(N_("Blue")); @@ -71,6 +79,57 @@ static void cmp_node_separate_color_update(bNodeTree *UNUSED(ntree), bNode *node node_cmp_combsep_color_label(&node->outputs, (CMPNodeCombSepColorMode)storage->mode); } +using namespace blender::realtime_compositor; + +class SeparateColorShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + } + + NodeCMPCombSepColor *get_node_combine_separate_color() + { + return static_cast<NodeCMPCombSepColor *>(bnode().storage); + } + + const char *get_shader_function_name() + { + switch (get_node_combine_separate_color()->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: + return "node_composite_separate_rgba"; + case CMP_NODE_COMBSEP_COLOR_HSV: + return "node_composite_separate_hsva"; + case CMP_NODE_COMBSEP_COLOR_HSL: + return "node_composite_separate_hsla"; + case CMP_NODE_COMBSEP_COLOR_YUV: + return "node_composite_separate_yuva_itu_709"; + case CMP_NODE_COMBSEP_COLOR_YCC: + switch (get_node_combine_separate_color()->ycc_mode) { + case BLI_YCC_ITU_BT601: + return "node_composite_separate_ycca_itu_601"; + case BLI_YCC_ITU_BT709: + return "node_composite_separate_ycca_itu_709"; + case BLI_YCC_JFIF_0_255: + return "node_composite_separate_ycca_jpeg"; + } + } + + BLI_assert_unreachable(); + return nullptr; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateColorShaderNode(node); +} + } // namespace blender::nodes::node_composite_separate_color_cc void register_node_type_cmp_separate_color() @@ -85,6 +144,7 @@ void register_node_type_cmp_separate_color() node_type_storage( &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); node_type_update(&ntype, file_ns::cmp_node_separate_color_update); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } @@ -95,22 +155,30 @@ 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_("Red")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(0); b.add_input<decl::Float>(N_("Green")) .default_value(0.0f) .min(0.0f) .max(1.0f) - .subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); b.add_input<decl::Float>(N_("Blue")) .default_value(0.0f) .min(0.0f) .max(1.0f) - .subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(2); b.add_input<decl::Float>(N_("Alpha")) .default_value(1.0f) .min(0.0f) .max(1.0f) - .subtype(PROP_FACTOR); + .subtype(PROP_FACTOR) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } @@ -120,6 +188,57 @@ static void cmp_node_combine_color_update(bNodeTree *UNUSED(ntree), bNode *node) node_cmp_combsep_color_label(&node->inputs, (CMPNodeCombSepColorMode)storage->mode); } +using namespace blender::realtime_compositor; + +class CombineColorShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + } + + NodeCMPCombSepColor *get_node_combine_separate_color() + { + return static_cast<NodeCMPCombSepColor *>(bnode().storage); + } + + const char *get_shader_function_name() + { + switch (get_node_combine_separate_color()->mode) { + case CMP_NODE_COMBSEP_COLOR_RGB: + return "node_composite_combine_rgba"; + case CMP_NODE_COMBSEP_COLOR_HSV: + return "node_composite_combine_hsva"; + case CMP_NODE_COMBSEP_COLOR_HSL: + return "node_composite_combine_hsla"; + case CMP_NODE_COMBSEP_COLOR_YUV: + return "node_composite_combine_yuva_itu_709"; + case CMP_NODE_COMBSEP_COLOR_YCC: + switch (get_node_combine_separate_color()->ycc_mode) { + case BLI_YCC_ITU_BT601: + return "node_composite_combine_ycca_itu_601"; + case BLI_YCC_ITU_BT709: + return "node_composite_combine_ycca_itu_709"; + case BLI_YCC_JFIF_0_255: + return "node_composite_combine_ycca_jpeg"; + } + } + + BLI_assert_unreachable(); + return nullptr; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineColorShaderNode(node); +} + } // namespace blender::nodes::node_composite_combine_color_cc void register_node_type_cmp_combine_color() @@ -134,6 +253,7 @@ void register_node_type_cmp_combine_color() node_type_storage( &ntype, "NodeCMPCombSepColor", node_free_standard_storage, node_copy_standard_storage); node_type_update(&ntype, file_ns::cmp_node_combine_color_update); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; 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 a169f7e0dd3..b655c0db106 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_hsva.cc @@ -5,60 +5,112 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE HSVA ******************** */ -namespace blender::nodes::node_composite_sepcomb_hsva_cc { +namespace blender::nodes::node_composite_separate_hsva_cc { static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("H")); b.add_output<decl::Float>(N_("S")); b.add_output<decl::Float>(N_("V")); b.add_output<decl::Float>(N_("A")); } -} // namespace blender::nodes::node_composite_sepcomb_hsva_cc +using namespace blender::realtime_compositor; + +class SeparateHSVAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_separate_hsva", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateHSVAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_hsva_cc void register_node_type_cmp_sephsva() { - namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc; + namespace file_ns = blender::nodes::node_composite_separate_hsva_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA_LEGACY, "Separate HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sephsva_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE HSVA ******************** */ -namespace blender::nodes::node_composite_sepcomb_hsva_cc { +namespace blender::nodes::node_composite_combine_hsva_cc { static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("H")).min(0.0f).max(1.0f).compositor_domain_priority(0); + b.add_input<decl::Float>(N_("S")).min(0.0f).max(1.0f).compositor_domain_priority(1); + b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2); + b.add_input<decl::Float>(N_("A")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes::node_composite_sepcomb_hsva_cc +using namespace blender::realtime_compositor; + +class CombineHSVAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_combine_hsva", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineHSVAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_hsva_cc void register_node_type_cmp_combhsva() { - namespace file_ns = blender::nodes::node_composite_sepcomb_hsva_cc; + namespace file_ns = blender::nodes::node_composite_combine_hsva_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA_LEGACY, "Combine HSVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combhsva_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; 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 a243500b56d..1f4c9fd153f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_rgba.cc @@ -5,59 +5,112 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE RGBA ******************** */ -namespace blender::nodes::node_composite_sepcomb_rgba_cc { + +namespace blender::nodes::node_composite_separate_rgba_cc { static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("R")); b.add_output<decl::Float>(N_("G")); b.add_output<decl::Float>(N_("B")); b.add_output<decl::Float>(N_("A")); } -} // namespace blender::nodes::node_composite_sepcomb_rgba_cc +using namespace blender::realtime_compositor; + +class SeparateRGBAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_separate_rgba", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateRGBAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_rgba_cc void register_node_type_cmp_seprgba() { - namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc; + namespace file_ns = blender::nodes::node_composite_separate_rgba_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA_LEGACY, "Separate RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_seprgba_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE RGBA ******************** */ -namespace blender::nodes::node_composite_sepcomb_rgba_cc { +namespace blender::nodes::node_composite_combine_rgba_cc { static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("R")).min(0.0f).max(1.0f).compositor_domain_priority(0); + b.add_input<decl::Float>(N_("G")).min(0.0f).max(1.0f).compositor_domain_priority(1); + b.add_input<decl::Float>(N_("B")).min(0.0f).max(1.0f).compositor_domain_priority(2); + b.add_input<decl::Float>(N_("A")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes::node_composite_sepcomb_rgba_cc +using namespace blender::realtime_compositor; + +class CombineRGBAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_combine_rgba", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineRGBAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_rgba_cc void register_node_type_cmp_combrgba() { - namespace file_ns = blender::nodes::node_composite_sepcomb_rgba_cc; + namespace file_ns = blender::nodes::node_composite_combine_rgba_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA_LEGACY, "Combine RGBA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combrgba_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc index 4979c376cab..e288e698808 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_xyz.cc @@ -5,10 +5,15 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE XYZ ******************** */ -namespace blender::nodes { + +namespace blender::nodes::node_composite_separate_xyz_cc { static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b) { @@ -18,21 +23,44 @@ static void cmp_node_separate_xyz_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>("Z"); } -} // namespace blender::nodes +using namespace blender::realtime_compositor; + +class SeparateXYZShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_separate_xyz", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateXYZShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_xyz_cc void register_node_type_cmp_separate_xyz() { + namespace file_ns = blender::nodes::node_composite_separate_xyz_cc; + static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPARATE_XYZ, "Separate XYZ", NODE_CLASS_CONVERTER); - ntype.declare = blender::nodes::cmp_node_separate_xyz_declare; + ntype.declare = file_ns::cmp_node_separate_xyz_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE XYZ ******************** */ -namespace blender::nodes { +namespace blender::nodes::node_composite_combine_xyz_cc { static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b) { @@ -42,14 +70,37 @@ static void cmp_node_combine_xyz_declare(NodeDeclarationBuilder &b) b.add_output<decl::Vector>("Vector"); } -} // namespace blender::nodes +using namespace blender::realtime_compositor; + +class CombineXYZShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_combine_xyz", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineXYZShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_xyz_cc void register_node_type_cmp_combine_xyz() { + namespace file_ns = blender::nodes::node_composite_combine_xyz_cc; + static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBINE_XYZ, "Combine XYZ", NODE_CLASS_CONVERTER); - ntype.declare = blender::nodes::cmp_node_combine_xyz_declare; + ntype.declare = file_ns::cmp_node_combine_xyz_declare; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; 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 51d3c18d238..bebe6abe115 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_ycca.cc @@ -5,15 +5,23 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE YCCA ******************** */ -namespace blender::nodes::node_composite_sepcomb_ycca_cc { +namespace blender::nodes::node_composite_separate_ycca_cc { static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Y")); b.add_output<decl::Float>(N_("Cb")); b.add_output<decl::Float>(N_("Cr")); @@ -25,11 +33,51 @@ static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *nod node->custom1 = 1; /* BLI_YCC_ITU_BT709 */ } -} // namespace blender::nodes::node_composite_sepcomb_ycca_cc +using namespace blender::realtime_compositor; + +class SeparateYCCAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + } + + int get_mode() + { + return bnode().custom1; + } + + const char *get_shader_function_name() + { + switch (get_mode()) { + case BLI_YCC_ITU_BT601: + return "node_composite_separate_ycca_itu_601"; + case BLI_YCC_ITU_BT709: + return "node_composite_separate_ycca_itu_709"; + case BLI_YCC_JFIF_0_255: + return "node_composite_separate_ycca_jpeg"; + } + + BLI_assert_unreachable(); + return nullptr; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateYCCAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_ycca_cc void register_node_type_cmp_sepycca() { - namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc; + namespace file_ns = blender::nodes::node_composite_separate_ycca_cc; static bNodeType ntype; @@ -37,20 +85,33 @@ void register_node_type_cmp_sepycca() ntype.declare = file_ns::cmp_node_sepycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_sepycca); ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE YCCA ******************** */ -namespace blender::nodes::node_composite_sepcomb_ycca_cc { +namespace blender::nodes::node_composite_combine_ycca_cc { static void cmp_node_combycca_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Cb")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(1); + b.add_input<decl::Float>(N_("Cr")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(2); + b.add_input<decl::Float>(N_("A")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } @@ -59,11 +120,51 @@ static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *no node->custom1 = 1; /* BLI_YCC_ITU_BT709 */ } -} // namespace blender::nodes::node_composite_sepcomb_ycca_cc +using namespace blender::realtime_compositor; + +class CombineYCCAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), get_shader_function_name(), inputs, outputs); + } + + int get_mode() + { + return bnode().custom1; + } + + const char *get_shader_function_name() + { + switch (get_mode()) { + case BLI_YCC_ITU_BT601: + return "node_composite_combine_ycca_itu_601"; + case BLI_YCC_ITU_BT709: + return "node_composite_combine_ycca_itu_709"; + case BLI_YCC_JFIF_0_255: + return "node_composite_combine_ycca_jpeg"; + } + + BLI_assert_unreachable(); + return nullptr; + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineYCCAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_ycca_cc void register_node_type_cmp_combycca() { - namespace file_ns = blender::nodes::node_composite_sepcomb_ycca_cc; + namespace file_ns = blender::nodes::node_composite_combine_ycca_cc; static bNodeType ntype; @@ -71,6 +172,7 @@ void register_node_type_cmp_combycca() ntype.declare = file_ns::cmp_node_combycca_declare; node_type_init(&ntype, file_ns::node_composit_init_mode_combycca); ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } 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 4acd2294114..1f0eb04cfc3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sepcomb_yuva.cc @@ -5,60 +5,112 @@ * \ingroup cmpnodes */ +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SEPARATE YUVA ******************** */ -namespace blender::nodes::node_composite_sepcomb_yuva_cc { +namespace blender::nodes::node_composite_separate_yuva_cc { static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Y")); b.add_output<decl::Float>(N_("U")); b.add_output<decl::Float>(N_("V")); b.add_output<decl::Float>(N_("A")); } -} // namespace blender::nodes::node_composite_sepcomb_yuva_cc +using namespace blender::realtime_compositor; + +class SeparateYUVAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_separate_yuva_itu_709", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SeparateYUVAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_separate_yuva_cc void register_node_type_cmp_sepyuva() { - namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc; + namespace file_ns = blender::nodes::node_composite_separate_yuva_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA_LEGACY, "Separate YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_sepyuva_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** COMBINE YUVA ******************** */ -namespace blender::nodes::node_composite_sepcomb_yuva_cc { +namespace blender::nodes::node_composite_combine_yuva_cc { static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f); - b.add_input<decl::Float>(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Float>(N_("Y")).min(0.0f).max(1.0f).compositor_domain_priority(0); + b.add_input<decl::Float>(N_("U")).min(0.0f).max(1.0f).compositor_domain_priority(1); + b.add_input<decl::Float>(N_("V")).min(0.0f).max(1.0f).compositor_domain_priority(2); + b.add_input<decl::Float>(N_("A")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(3); b.add_output<decl::Color>(N_("Image")); } -} // namespace blender::nodes::node_composite_sepcomb_yuva_cc +using namespace blender::realtime_compositor; + +class CombineYUVAShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + GPU_stack_link(material, &bnode(), "node_composite_combine_yuva_itu_709", inputs, outputs); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new CombineYUVAShaderNode(node); +} + +} // namespace blender::nodes::node_composite_combine_yuva_cc void register_node_type_cmp_combyuva() { - namespace file_ns = blender::nodes::node_composite_sepcomb_yuva_cc; + namespace file_ns = blender::nodes::node_composite_combine_yuva_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA_LEGACY, "Combine YUVA", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_combyuva_declare; ntype.gather_link_search_ops = nullptr; + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 8aeaafbbf67..9930125aa70 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.cc +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -8,6 +8,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" /* **************** SET ALPHA ******************** */ @@ -16,8 +20,14 @@ namespace blender::nodes::node_composite_setalpha_cc { static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("Alpha")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .compositor_domain_priority(1); b.add_output<decl::Color>(N_("Image")); } @@ -33,6 +43,36 @@ static void node_composit_buts_set_alpha(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "mode", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class SetAlphaShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + if (get_node_set_alpha()->mode == CMP_NODE_SETALPHA_MODE_APPLY) { + GPU_stack_link(material, &bnode(), "node_composite_set_alpha_apply", inputs, outputs); + return; + } + + GPU_stack_link(material, &bnode(), "node_composite_set_alpha_replace", inputs, outputs); + } + + NodeSetAlpha *get_node_set_alpha() + { + return static_cast<NodeSetAlpha *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new SetAlphaShaderNode(node); +} + } // namespace blender::nodes::node_composite_setalpha_cc void register_node_type_cmp_setalpha() @@ -47,6 +87,7 @@ void register_node_type_cmp_setalpha() node_type_init(&ntype, file_ns::node_composit_init_setalpha); node_type_storage( &ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc index ab325c4559f..085de69e63e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_split_viewer.cc @@ -11,6 +11,12 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** SPLIT VIEWER ******************** */ @@ -43,6 +49,70 @@ static void node_composit_buts_splitviewer(uiLayout *layout, bContext *UNUSED(C) uiItemR(col, ptr, "factor", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ViewerOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + GPUShader *shader = get_split_viewer_shader(); + GPU_shader_bind(shader); + + const int2 size = compute_domain().size; + + GPU_shader_uniform_1f(shader, "split_ratio", get_split_ratio()); + GPU_shader_uniform_2iv(shader, "view_size", size); + + const Result &first_image = get_input("Image"); + first_image.bind_as_texture(shader, "first_image_tx"); + const Result &second_image = get_input("Image_001"); + second_image.bind_as_texture(shader, "second_image_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, size); + + first_image.unbind_as_texture(); + second_image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } + + GPUShader *get_split_viewer_shader() + { + if (get_split_axis() == CMP_NODE_SPLIT_VIEWER_HORIZONTAL) { + return shader_manager().get("compositor_split_viewer_horizontal"); + } + + return shader_manager().get("compositor_split_viewer_vertical"); + } + + CMPNodeSplitViewerAxis get_split_axis() + { + return (CMPNodeSplitViewerAxis)bnode().custom2; + } + + float get_split_ratio() + { + return bnode().custom1 / 100.0f; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ViewerOperation(context, node); +} + } // namespace blender::nodes::node_composite_split_viewer_cc void register_node_type_cmp_splitviewer() @@ -57,6 +127,7 @@ void register_node_type_cmp_splitviewer() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_splitviewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.no_muting = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc index 63d00a0864b..75a96a05863 100644 --- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc +++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc @@ -11,6 +11,8 @@ #include "BKE_context.h" #include "BKE_lib_id.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Stabilize 2D ******************** */ @@ -58,6 +60,23 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe uiItemR(layout, ptr, "invert", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class Stabilize2DOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new Stabilize2DOperation(context, node); +} + } // namespace blender::nodes::node_composite_stabilize2d_cc void register_node_type_cmp_stabilize2d() @@ -70,6 +89,7 @@ void register_node_type_cmp_stabilize2d() ntype.declare = file_ns::cmp_node_stabilize2d_declare; ntype.draw_buttons = file_ns::node_composit_buts_stabilize2d; ntype.initfunc_api = file_ns::init; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc index 766f26745ef..4b9264d7e35 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc +++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_sunbeams_cc { @@ -38,6 +40,23 @@ static void node_composit_buts_sunbeams(uiLayout *layout, bContext *UNUSED(C), P ICON_NONE); } +using namespace blender::realtime_compositor; + +class SunBeamsOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new SunBeamsOperation(context, node); +} + } // namespace blender::nodes::node_composite_sunbeams_cc void register_node_type_cmp_sunbeams() @@ -52,6 +71,7 @@ void register_node_type_cmp_sunbeams() node_type_init(&ntype, file_ns::init); node_type_storage( &ntype, "NodeSunBeams", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.cc b/source/blender/nodes/composite/nodes/node_composite_switch.cc index bda490572e9..767802cc442 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switch.cc +++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Switch ******************** */ @@ -26,6 +28,30 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "check", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class SwitchOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input(get_condition() ? "On" : "Off"); + Result &result = get_result("Image"); + input.pass_through(result); + } + + bool get_condition() + { + return bnode().custom1; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new SwitchOperation(context, node); +} + } // namespace blender::nodes::node_composite_switch_cc void register_node_type_cmp_switch() @@ -38,5 +64,7 @@ void register_node_type_cmp_switch() ntype.declare = file_ns::cmp_node_switch_declare; ntype.draw_buttons = file_ns::node_composit_buts_switch; node_type_size_preset(&ntype, NODE_SIZE_SMALL); + ntype.get_compositor_operation = file_ns::get_compositor_operation; + nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.cc b/source/blender/nodes/composite/nodes/node_composite_switchview.cc index 2cf3da03a05..e74c3b6007a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switchview.cc +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc @@ -11,6 +11,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** SWITCH VIEW ******************** */ @@ -140,6 +142,25 @@ static void node_composit_buts_switch_view_ex(uiLayout *layout, nullptr); } +using namespace blender::realtime_compositor; + +class SwitchViewOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input(context().get_view_name()); + Result &result = get_result("Image"); + input.pass_through(result); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new SwitchViewOperation(context, node); +} + } // namespace blender::nodes::node_composite_switchview_cc void register_node_type_cmp_switch_view() @@ -153,6 +174,7 @@ void register_node_type_cmp_switch_view() ntype.draw_buttons_ex = file_ns::node_composit_buts_switch_view_ex; ntype.initfunc_api = file_ns::init_switch_view; node_type_update(&ntype, file_ns::cmp_node_switch_view_update); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.cc b/source/blender/nodes/composite/nodes/node_composite_texture.cc index 7571e97a2cd..5a628aae7a7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.cc +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** TEXTURE ******************** */ @@ -23,6 +25,24 @@ static void cmp_node_texture_declare(NodeDeclarationBuilder &b) b.add_output<decl::Color>(N_("Color")); } +using namespace blender::realtime_compositor; + +class TextureOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("Value").allocate_invalid(); + get_result("Color").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TextureOperation(context, node); +} + } // namespace blender::nodes::node_composite_texture_cc void register_node_type_cmp_texture() @@ -34,6 +54,7 @@ void register_node_type_cmp_texture() cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT); ntype.declare = file_ns::cmp_node_texture_declare; ntype.flag |= NODE_PREVIEW; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc index cdfe97b038d..4cc3d4f32a3 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.cc +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -10,6 +10,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_tonemap_cc { @@ -58,6 +60,23 @@ static void node_composit_buts_tonemap(uiLayout *layout, bContext *UNUSED(C), Po } } +using namespace blender::realtime_compositor; + +class ToneMapOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ToneMapOperation(context, node); +} + } // namespace blender::nodes::node_composite_tonemap_cc void register_node_type_cmp_tonemap() @@ -71,6 +90,7 @@ void register_node_type_cmp_tonemap() ntype.draw_buttons = file_ns::node_composit_buts_tonemap; node_type_init(&ntype, file_ns::node_composit_init_tonemap); node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc index 0e99ff59327..0e9bd800f44 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.cc +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -18,6 +18,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" namespace blender::nodes::node_composite_trackpos_cc { @@ -102,6 +104,25 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN } } +using namespace blender::realtime_compositor; + +class TrackPositionOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_result("X").allocate_invalid(); + get_result("Y").allocate_invalid(); + get_result("Speed").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TrackPositionOperation(context, node); +} + } // namespace blender::nodes::node_composite_trackpos_cc void register_node_type_cmp_trackpos() @@ -116,6 +137,7 @@ void register_node_type_cmp_trackpos() ntype.initfunc_api = file_ns::init; node_type_storage( &ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.cc b/source/blender/nodes/composite/nodes/node_composite_transform.cc index fe72f5e33ca..7c5866d2d06 100644 --- a/source/blender/nodes/composite/nodes/node_composite_transform.cc +++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc @@ -5,9 +5,15 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" +#include "BLI_float3x3.hh" +#include "BLI_math_vector.h" + #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Transform ******************** */ @@ -16,15 +22,30 @@ namespace blender::nodes::node_composite_transform_cc { static void cmp_node_transform_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("X")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Y")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_expects_single_value(); b.add_input<decl::Float>(N_("Angle")) .default_value(0.0f) .min(-10000.0f) .max(10000.0f) - .subtype(PROP_ANGLE); - b.add_input<decl::Float>(N_("Scale")).default_value(1.0f).min(0.0001f).max(CMP_SCALE_MAX); + .subtype(PROP_ANGLE) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Scale")) + .default_value(1.0f) + .min(0.0001f) + .max(CMP_SCALE_MAX) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } @@ -33,6 +54,51 @@ static void node_composit_buts_transform(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "filter_type", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); } +using namespace blender::realtime_compositor; + +class TransformOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input("Image"); + Result &result = get_result("Image"); + input.pass_through(result); + + const float2 translation = float2(get_input("X").get_float_value_default(0.0f), + get_input("Y").get_float_value_default(0.0f)); + const float rotation = get_input("Angle").get_float_value_default(0.0f); + const float2 scale = float2(get_input("Scale").get_float_value_default(1.0f)); + + const float3x3 transformation = float3x3::from_translation_rotation_scale( + translation, rotation, scale); + + result.transform(transformation); + result.get_realization_options().interpolation = get_interpolation(); + } + + Interpolation get_interpolation() + { + switch (bnode().custom1) { + case 0: + return Interpolation::Nearest; + case 1: + return Interpolation::Bilinear; + case 2: + return Interpolation::Bicubic; + } + + BLI_assert_unreachable(); + return Interpolation::Nearest; + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TransformOperation(context, node); +} + } // namespace blender::nodes::node_composite_transform_cc void register_node_type_cmp_transform() @@ -44,6 +110,7 @@ void register_node_type_cmp_transform() cmp_node_type_base(&ntype, CMP_NODE_TRANSFORM, "Transform", NODE_CLASS_DISTORT); ntype.declare = file_ns::cmp_node_transform_declare; ntype.draw_buttons = file_ns::node_composit_buts_transform; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.cc b/source/blender/nodes/composite/nodes/node_composite_translate.cc index bbdc8ca4d31..fbd53b8310f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_translate.cc +++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc @@ -5,9 +5,14 @@ * \ingroup cmpnodes */ +#include "BLI_float3x3.hh" +#include "BLI_math_vec_types.hh" + #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -16,9 +21,19 @@ namespace blender::nodes::node_composite_translate_cc { static void cmp_node_translate_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); - b.add_input<decl::Float>(N_("X")).default_value(0.0f).min(-10000.0f).max(10000.0f); - b.add_input<decl::Float>(N_("Y")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input<decl::Color>(N_("Image")) + .default_value({1.0f, 1.0f, 1.0f, 1.0f}) + .compositor_domain_priority(0); + b.add_input<decl::Float>(N_("X")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_expects_single_value(); + b.add_input<decl::Float>(N_("Y")) + .default_value(0.0f) + .min(-10000.0f) + .max(10000.0f) + .compositor_expects_single_value(); b.add_output<decl::Color>(N_("Image")); } @@ -34,6 +49,59 @@ static void node_composit_buts_translate(uiLayout *layout, bContext *UNUSED(C), uiItemR(layout, ptr, "wrap_axis", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class TranslateOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &input = get_input("Image"); + Result &result = get_result("Image"); + input.pass_through(result); + + float x = get_input("X").get_float_value_default(0.0f); + float y = get_input("Y").get_float_value_default(0.0f); + if (get_use_relative()) { + x *= input.domain().size.x; + y *= input.domain().size.y; + } + + const float2 translation = float2(x, y); + const float3x3 transformation = float3x3::from_translation(translation); + + result.transform(transformation); + result.get_realization_options().repeat_x = get_repeat_x(); + result.get_realization_options().repeat_y = get_repeat_y(); + } + + NodeTranslateData &get_node_translate() + { + return *static_cast<NodeTranslateData *>(bnode().storage); + } + + bool get_use_relative() + { + return get_node_translate().relative; + } + + bool get_repeat_x() + { + return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY); + } + + bool get_repeat_y() + { + return ELEM(get_node_translate().wrap_axis, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new TranslateOperation(context, node); +} + } // namespace blender::nodes::node_composite_translate_cc void register_node_type_cmp_translate() @@ -48,6 +116,7 @@ void register_node_type_cmp_translate() node_type_init(&ntype, file_ns::node_composit_init_translate); node_type_storage( &ntype, "NodeTranslateData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc index df669d5beda..03a7bc61924 100644 --- a/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_val_to_rgb.cc @@ -5,18 +5,33 @@ * \ingroup cmpnodes */ +#include "BLI_assert.h" + +#include "IMB_colormanagement.h" + +#include "BKE_colorband.h" + +#include "GPU_material.h" + +#include "COM_shader_node.hh" + #include "node_composite_util.hh" #include "BKE_colorband.h" /* **************** VALTORGB ******************** */ -namespace blender::nodes::node_composite_val_to_rgb_cc { +namespace blender::nodes::node_composite_color_ramp_cc { static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Float>(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output<decl::Color>(N_("Image")); + b.add_input<decl::Float>(N_("Fac")) + .default_value(0.5f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .compositor_domain_priority(1); + b.add_output<decl::Color>(N_("Image")).compositor_domain_priority(0); b.add_output<decl::Float>(N_("Alpha")); } @@ -25,11 +40,94 @@ static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node) node->storage = BKE_colorband_add(true); } -} // namespace blender::nodes::node_composite_val_to_rgb_cc +using namespace blender::realtime_compositor; + +class ColorRampShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + struct ColorBand *color_band = get_color_band(); + + /* Common / easy case optimization. */ + if ((color_band->tot <= 2) && (color_band->color_mode == COLBAND_BLEND_RGB)) { + float mul_bias[2]; + switch (color_band->ipotype) { + case COLBAND_INTERP_LINEAR: + mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos); + mul_bias[1] = -mul_bias[0] * color_band->data[0].pos; + GPU_stack_link(material, + &bnode(), + "valtorgb_opti_linear", + inputs, + outputs, + GPU_uniform(mul_bias), + GPU_uniform(&color_band->data[0].r), + GPU_uniform(&color_band->data[1].r)); + return; + case COLBAND_INTERP_CONSTANT: + mul_bias[1] = max_ff(color_band->data[0].pos, color_band->data[1].pos); + GPU_stack_link(material, + &bnode(), + "valtorgb_opti_constant", + inputs, + outputs, + GPU_uniform(&mul_bias[1]), + GPU_uniform(&color_band->data[0].r), + GPU_uniform(&color_band->data[1].r)); + return; + case COLBAND_INTERP_EASE: + mul_bias[0] = 1.0f / (color_band->data[1].pos - color_band->data[0].pos); + mul_bias[1] = -mul_bias[0] * color_band->data[0].pos; + GPU_stack_link(material, + &bnode(), + "valtorgb_opti_ease", + inputs, + outputs, + GPU_uniform(mul_bias), + GPU_uniform(&color_band->data[0].r), + GPU_uniform(&color_band->data[1].r)); + return; + default: + BLI_assert_unreachable(); + return; + } + } + + float *array, layer; + int size; + BKE_colorband_evaluate_table_rgba(color_band, &array, &size); + GPUNodeLink *tex = GPU_color_band(material, size, array, &layer); + + if (color_band->ipotype == COLBAND_INTERP_CONSTANT) { + GPU_stack_link( + material, &bnode(), "valtorgb_nearest", inputs, outputs, tex, GPU_constant(&layer)); + return; + } + + GPU_stack_link(material, &bnode(), "valtorgb", inputs, outputs, tex, GPU_constant(&layer)); + } + + struct ColorBand *get_color_band() + { + return static_cast<struct ColorBand *>(bnode().storage); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new ColorRampShaderNode(node); +} + +} // namespace blender::nodes::node_composite_color_ramp_cc void register_node_type_cmp_valtorgb() { - namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc; + namespace file_ns = blender::nodes::node_composite_color_ramp_cc; static bNodeType ntype; @@ -38,31 +136,63 @@ void register_node_type_cmp_valtorgb() node_type_size(&ntype, 240, 200, 320); node_type_init(&ntype, file_ns::node_composit_init_valtorgb); node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } /* **************** RGBTOBW ******************** */ -namespace blender::nodes::node_composite_val_to_rgb_cc { +namespace blender::nodes::node_composite_rgb_to_bw_cc { static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b) { - b.add_input<decl::Color>(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input<decl::Color>(N_("Image")) + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .compositor_domain_priority(0); b.add_output<decl::Float>(N_("Val")); } -} // namespace blender::nodes::node_composite_val_to_rgb_cc +using namespace blender::realtime_compositor; + +class RGBToBWShaderNode : public ShaderNode { + public: + using ShaderNode::ShaderNode; + + void compile(GPUMaterial *material) override + { + GPUNodeStack *inputs = get_inputs_array(); + GPUNodeStack *outputs = get_outputs_array(); + + float luminance_coefficients[3]; + IMB_colormanagement_get_luminance_coefficients(luminance_coefficients); + + GPU_stack_link(material, + &bnode(), + "color_to_luminance", + inputs, + outputs, + GPU_constant(luminance_coefficients)); + } +}; + +static ShaderNode *get_compositor_shader_node(DNode node) +{ + return new RGBToBWShaderNode(node); +} + +} // namespace blender::nodes::node_composite_rgb_to_bw_cc void register_node_type_cmp_rgbtobw() { - namespace file_ns = blender::nodes::node_composite_val_to_rgb_cc; + namespace file_ns = blender::nodes::node_composite_rgb_to_bw_cc; static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER); ntype.declare = file_ns::cmp_node_rgbtobw_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); + ntype.get_compositor_shader_node = file_ns::get_compositor_shader_node; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_value.cc b/source/blender/nodes/composite/nodes/node_composite_value.cc index a3269d3d1c2..a96e1db14ad 100644 --- a/source/blender/nodes/composite/nodes/node_composite_value.cc +++ b/source/blender/nodes/composite/nodes/node_composite_value.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** VALUE ******************** */ @@ -16,6 +18,29 @@ static void cmp_node_value_declare(NodeDeclarationBuilder &b) b.add_output<decl::Float>(N_("Value")).default_value(0.5f); } +using namespace blender::realtime_compositor; + +class ValueOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + Result &result = get_result("Value"); + result.allocate_single_value(); + + const bNodeSocket *socket = static_cast<bNodeSocket *>(bnode().outputs.first); + float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value; + + result.set_float_value(value); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ValueOperation(context, node); +} + } // namespace blender::nodes::node_composite_value_cc void register_node_type_cmp_value() @@ -27,6 +52,7 @@ void register_node_type_cmp_value() cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT); ntype.declare = file_ns::cmp_node_value_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc index 741f2e0e816..515478da75d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc +++ b/source/blender/nodes/composite/nodes/node_composite_vec_blur.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** VECTOR BLUR ******************** */ @@ -51,6 +53,23 @@ static void node_composit_buts_vecblur(uiLayout *layout, bContext *UNUSED(C), Po uiItemR(layout, ptr, "use_curved", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class VectorBlurOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new VectorBlurOperation(context, node); +} + } // namespace blender::nodes::node_composite_vec_blur_cc void register_node_type_cmp_vecblur() @@ -65,6 +84,7 @@ void register_node_type_cmp_vecblur() node_type_init(&ntype, file_ns::node_composit_init_vecblur); node_type_storage( &ntype, "NodeBlurData", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.cc b/source/blender/nodes/composite/nodes/node_composite_viewer.cc index 05f395183b5..4e82b31ca47 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.cc +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -5,6 +5,8 @@ * \ingroup cmpnodes */ +#include "BLI_math_vec_types.hh" + #include "BKE_global.h" #include "BKE_image.h" @@ -13,6 +15,13 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "GPU_shader.h" +#include "GPU_state.h" +#include "GPU_texture.h" + +#include "COM_node_operation.hh" +#include "COM_utilities.hh" + #include "node_composite_util.hh" /* **************** VIEWER ******************** */ @@ -55,6 +64,125 @@ static void node_composit_buts_viewer_ex(uiLayout *layout, bContext *UNUSED(C), } } +using namespace blender::realtime_compositor; + +class ViewerOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + if (image.is_single_value() && alpha.is_single_value()) { + execute_clear(); + } + else if (ignore_alpha()) { + execute_ignore_alpha(); + } + else if (!node().input_by_identifier("Alpha")->is_logically_linked()) { + execute_copy(); + } + else { + execute_set_alpha(); + } + } + + /* Executes when all inputs are single values, in which case, the output texture can just be + * cleared to the appropriate color. */ + void execute_clear() + { + const Result &image = get_input("Image"); + const Result &alpha = get_input("Alpha"); + + float4 color = image.get_color_value(); + if (ignore_alpha()) { + color.w = 1.0f; + } + else if (node().input_by_identifier("Alpha")->is_logically_linked()) { + color.w = alpha.get_float_value(); + } + + GPU_texture_clear(context().get_output_texture(), GPU_DATA_FLOAT, color); + } + + /* Executes when the alpha channel of the image is ignored. */ + void execute_ignore_alpha() + { + GPUShader *shader = shader_manager().get("compositor_convert_color_to_opaque"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "input_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* Executes when the image texture is written with no adjustments and can thus be copied directly + * to the output texture. */ + void execute_copy() + { + const Result &image = get_input("Image"); + + /* Make sure any prior writes to the texture are reflected before copying it. */ + GPU_memory_barrier(GPU_BARRIER_TEXTURE_UPDATE); + + GPU_texture_copy(context().get_output_texture(), image.texture()); + } + + /* Executes when the alpha channel of the image is set as the value of the input alpha. */ + void execute_set_alpha() + { + GPUShader *shader = shader_manager().get("compositor_set_alpha"); + GPU_shader_bind(shader); + + const Result &image = get_input("Image"); + image.bind_as_texture(shader, "image_tx"); + + const Result &alpha = get_input("Alpha"); + alpha.bind_as_texture(shader, "alpha_tx"); + + GPUTexture *output_texture = context().get_output_texture(); + const int image_unit = GPU_shader_get_texture_binding(shader, "output_img"); + GPU_texture_image_bind(output_texture, image_unit); + + compute_dispatch_threads_at_least(shader, compute_domain().size); + + image.unbind_as_texture(); + alpha.unbind_as_texture(); + GPU_texture_image_unbind(output_texture); + GPU_shader_unbind(); + } + + /* If true, the alpha channel of the image is set to 1, that is, it becomes opaque. If false, the + * alpha channel of the image is retained, but only if the alpha input is not linked. If the + * alpha input is linked, it the value of that input will be used as the alpha of the image. */ + bool ignore_alpha() + { + return bnode().custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA; + } + + /* The operation domain have the same dimensions of the output without any transformations. */ + Domain compute_domain() override + { + return Domain(context().get_output_size()); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ViewerOperation(context, node); +} + } // namespace blender::nodes::node_composite_viewer_cc void register_node_type_cmp_viewer() @@ -70,6 +198,7 @@ void register_node_type_cmp_viewer() ntype.flag |= NODE_PREVIEW; node_type_init(&ntype, file_ns::node_composit_init_viewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); + ntype.get_compositor_operation = file_ns::get_compositor_operation; ntype.no_muting = true; diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc index be90aeb7acc..e5f460099e9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_zcombine.cc +++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc @@ -8,6 +8,8 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "COM_node_operation.hh" + #include "node_composite_util.hh" /* **************** Z COMBINE ******************** */ @@ -33,6 +35,24 @@ static void node_composit_buts_zcombine(uiLayout *layout, bContext *UNUSED(C), P uiItemR(col, ptr, "use_antialias_z", UI_ITEM_R_SPLIT_EMPTY_NAME, nullptr, ICON_NONE); } +using namespace blender::realtime_compositor; + +class ZCombineOperation : public NodeOperation { + public: + using NodeOperation::NodeOperation; + + void execute() override + { + get_input("Image").pass_through(get_result("Image")); + get_result("Z").allocate_invalid(); + } +}; + +static NodeOperation *get_compositor_operation(Context &context, DNode node) +{ + return new ZCombineOperation(context, node); +} + } // namespace blender::nodes::node_composite_zcombine_cc void register_node_type_cmp_zcombine() @@ -44,6 +64,7 @@ void register_node_type_cmp_zcombine() cmp_node_type_base(&ntype, CMP_NODE_ZCOMBINE, "Z Combine", NODE_CLASS_OP_COLOR); ntype.declare = file_ns::cmp_node_zcombine_declare; ntype.draw_buttons = file_ns::node_composit_buts_zcombine; + ntype.get_compositor_operation = file_ns::get_compositor_operation; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index ddd8c8949b1..31c00cc6b82 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -76,8 +76,8 @@ set(SRC nodes/node_geo_input_index.cc nodes/node_geo_input_instance_rotation.cc nodes/node_geo_input_instance_scale.cc - nodes/node_geo_input_material_index.cc nodes/node_geo_input_material.cc + nodes/node_geo_input_material_index.cc nodes/node_geo_input_mesh_edge_angle.cc nodes/node_geo_input_mesh_edge_neighbors.cc nodes/node_geo_input_mesh_edge_vertices.cc @@ -118,9 +118,9 @@ set(SRC nodes/node_geo_mesh_to_points.cc nodes/node_geo_mesh_to_volume.cc nodes/node_geo_object_info.cc + nodes/node_geo_points.cc nodes/node_geo_points_to_vertices.cc nodes/node_geo_points_to_volume.cc - nodes/node_geo_points.cc nodes/node_geo_proximity.cc nodes/node_geo_raycast.cc nodes/node_geo_realize_instances.cc @@ -134,8 +134,8 @@ set(SRC nodes/node_geo_set_curve_radius.cc nodes/node_geo_set_curve_tilt.cc nodes/node_geo_set_id.cc - nodes/node_geo_set_material_index.cc nodes/node_geo_set_material.cc + nodes/node_geo_set_material_index.cc nodes/node_geo_set_point_radius.cc nodes/node_geo_set_position.cc nodes/node_geo_set_shade_smooth.cc diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index 38e914b9a9f..e3998322741 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -109,6 +109,7 @@ void register_node_tree_type_geo() MEM_callocN(sizeof(bNodeTreeType), "geometry node tree type")); tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); + strcpy(tt->group_idname, "GeometryNodeGroup"); strcpy(tt->ui_name, N_("Geometry Node Editor")); tt->ui_icon = ICON_GEOMETRY_NODES; strcpy(tt->ui_description, N_("Geometry nodes")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc index cfb9cbf7e24..a6c67cac916 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -220,11 +220,11 @@ BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); - const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + - v2_density_factor * bary_coord.z; + const float probability = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + + v2_density_factor * bary_coord.z; const float hash = noise::hash_float_to_float(bary_coord); - if (hash > probablity) { + if (hash > probability) { elimination_mask[i] = true; } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index acf85e74353..024dbd1c852 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -225,7 +225,7 @@ template<typename T, typename GetMixIndicesFn> void copy_with_mixing(MutableSpan<T> dst, Span<T> src, GetMixIndicesFn get_mix_indices_fn) { threading::parallel_for(dst.index_range(), 512, [&](const IndexRange range) { - attribute_math::DefaultPropatationMixer<T> mixer{dst.slice(range)}; + attribute_math::DefaultPropagationMixer<T> mixer{dst.slice(range)}; for (const int i_dst : IndexRange(range.size())) { for (const int i_src : get_mix_indices_fn(range[i_dst])) { mixer.mix_in(i_dst, src[i_src]); @@ -437,7 +437,7 @@ static void extrude_mesh_edges(MeshComponent &component, Array<float3> vert_offsets; if (!edge_offsets.is_single()) { vert_offsets.reinitialize(orig_vert_size); - attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets); + attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets); for (const int i_edge : edge_selection) { const MEdge &edge = orig_edges[i_edge]; const float3 offset = edge_offsets[i_edge]; @@ -583,7 +583,7 @@ static void extrude_mesh_edges(MeshComponent &component, /* Both corners on each vertical edge of the side polygon get the same value, * so there are only two unique values to mix. */ Array<T> side_poly_corner_data(2); - attribute_math::DefaultPropatationMixer<T> mixer{side_poly_corner_data}; + attribute_math::DefaultPropagationMixer<T> mixer{side_poly_corner_data}; const MEdge &duplicate_edge = duplicate_edges[i_edge_selection]; const int new_vert_1 = duplicate_edge.v1; @@ -705,7 +705,7 @@ static void extrude_mesh_face_regions(MeshComponent &component, Array<float3> vert_offsets; if (!poly_offsets.is_single()) { vert_offsets.reinitialize(orig_vert_size); - attribute_math::DefaultPropatationMixer<float3> mixer(vert_offsets); + attribute_math::DefaultPropagationMixer<float3> mixer(vert_offsets); for (const int i_poly : poly_selection) { const MPoly &poly = orig_polys[i_poly]; const float3 offset = poly_offsets[i_poly]; diff --git a/source/blender/nodes/shader/node_shader_tree.cc b/source/blender/nodes/shader/node_shader_tree.cc index 43dbf5060bd..02ac54f4b2b 100644 --- a/source/blender/nodes/shader/node_shader_tree.cc +++ b/source/blender/nodes/shader/node_shader_tree.cc @@ -166,6 +166,7 @@ void register_node_tree_type_sh() tt->type = NTREE_SHADER; strcpy(tt->idname, "ShaderNodeTree"); + strcpy(tt->group_idname, "ShaderNodeGroup"); strcpy(tt->ui_name, N_("Shader Editor")); tt->ui_icon = ICON_NODE_MATERIAL; strcpy(tt->ui_description, N_("Shader nodes")); diff --git a/source/blender/nodes/shader/nodes/node_shader_geometry.cc b/source/blender/nodes/shader/nodes/node_shader_geometry.cc index 47df932f9d4..d23561de7ff 100644 --- a/source/blender/nodes/shader/nodes/node_shader_geometry.cc +++ b/source/blender/nodes/shader/nodes/node_shader_geometry.cc @@ -29,10 +29,9 @@ static int node_shader_gpu_geometry(GPUMaterial *mat, if (out[5].hasoutput) { GPU_material_flag_set(mat, GPU_MATFLAG_BARYCENTRIC); } - /* Opti: don't request orco if not needed. */ + /* Optimization: don't request orco if not needed. */ const float val[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - GPUNodeLink *orco_link = (!out[2].hasoutput) ? GPU_constant(val) : - GPU_attribute(mat, CD_ORCO, ""); + GPUNodeLink *orco_link = out[2].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(val); const bool success = GPU_stack_link(mat, node, "node_geometry", in, out, orco_link); diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc index 11d23e47735..f46556291ce 100644 --- a/source/blender/nodes/shader/nodes/node_shader_hair_info.cc +++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.cc @@ -23,8 +23,8 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat, { /* Length: don't request length if not needed. */ static const float zero = 0; - GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) : - GPU_attribute(mat, CD_HAIRLENGTH, ""); + GPUNodeLink *length_link = out[2].hasoutput ? GPU_attribute(mat, CD_HAIRLENGTH, "") : + GPU_constant(&zero); return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link); } diff --git a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc index 12707623049..478a6812c36 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mix_rgb.cc @@ -32,7 +32,7 @@ static const char *gpu_shader_get_name(int mode) case MA_RAMP_SCREEN: return "mix_screen"; case MA_RAMP_DIV: - return "mix_div"; + return "mix_div_fallback"; case MA_RAMP_DIFF: return "mix_diff"; case MA_RAMP_DARK: @@ -70,18 +70,23 @@ static int gpu_shader_mix_rgb(GPUMaterial *mat, { const char *name = gpu_shader_get_name(node->custom1); - if (name != nullptr) { - int ret = GPU_stack_link(mat, node, name, in, out); - if (ret && node->custom2 & SHD_MIXRGB_CLAMP) { - const float min[3] = {0.0f, 0.0f, 0.0f}; - const float max[3] = {1.0f, 1.0f, 1.0f}; - GPU_link( - mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link); - } - return ret; + if (name == nullptr) { + return 0; } - return 0; + const float min = 0.0f; + const float max = 1.0f; + const GPUNodeLink *factor_link = in[0].link ? in[0].link : GPU_uniform(in[0].vec); + GPU_link(mat, "clamp_value", factor_link, GPU_constant(&min), GPU_constant(&max), &in[0].link); + + int ret = GPU_stack_link(mat, node, name, in, out); + + if (ret && node->custom2 & SHD_MIXRGB_CLAMP) { + const float min[3] = {0.0f, 0.0f, 0.0f}; + const float max[3] = {1.0f, 1.0f, 1.0f}; + GPU_link(mat, "clamp_color", out[0].link, GPU_constant(min), GPU_constant(max), &out[0].link); + } + return ret; } class MixRGBFunction : public fn::MultiFunction { diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_rgb.cc index 38acfab322f..3d28f5278a2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_rgb.cc @@ -17,11 +17,12 @@ static void node_declare(NodeDeclarationBuilder &b) static int gpu_shader_rgb(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, + GPUNodeStack * /*in*/, GPUNodeStack *out) { - GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0); - return GPU_stack_link(mat, node, "set_rgba", in, out, link); + const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first); + float *value = static_cast<bNodeSocketValueRGBA *>(socket->default_value)->value; + return GPU_link(mat, "set_rgba", GPU_uniform(value), &out->link); } } // namespace blender::nodes::node_shader_rgb_cc diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc index fb5971021fc..0a28b34902e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_coord.cc @@ -41,9 +41,9 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat, GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) : GPU_uniform(&dummy_matrix[0][0]); - /* Opti: don't request orco if not needed. */ + /* Optimization: don't request orco if not needed. */ float4 zero(0.0f); - GPUNodeLink *orco = (!out[0].hasoutput) ? GPU_constant(zero) : GPU_attribute(mat, CD_ORCO, ""); + GPUNodeLink *orco = out[0].hasoutput ? GPU_attribute(mat, CD_ORCO, "") : GPU_constant(zero); GPUNodeLink *mtface = GPU_attribute(mat, CD_AUTO_FROM_NAME, ""); GPU_stack_link(mat, node, "node_tex_coord", in, out, inv_obmat, orco, mtface); diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 362cdf58052..14dbb3b25eb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -17,11 +17,12 @@ static void sh_node_value_declare(NodeDeclarationBuilder &b) static int gpu_shader_value(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, + GPUNodeStack * /*in*/, GPUNodeStack *out) { - GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0); - return GPU_stack_link(mat, node, "set_value", in, out, link); + const bNodeSocket *socket = static_cast<bNodeSocket *>(node->outputs.first); + float value = static_cast<bNodeSocketValueFloat *>(socket->default_value)->value; + return GPU_link(mat, "set_value", GPU_uniform(&value), &out->link); } static void sh_node_value_build_multi_function(NodeMultiFunctionBuilder &builder) diff --git a/source/blender/nodes/texture/CMakeLists.txt b/source/blender/nodes/texture/CMakeLists.txt index 5bed54ebfd7..2ccdf4c0bc9 100644 --- a/source/blender/nodes/texture/CMakeLists.txt +++ b/source/blender/nodes/texture/CMakeLists.txt @@ -15,6 +15,7 @@ set(INC ../../render ../../windowmanager ../../../../intern/guardedalloc + ../../bmesh # RNA_prototypes.h ${CMAKE_BINARY_DIR}/source/blender/makesrna ) diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 03dc61af9a2..ac105b5bcb9 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -140,6 +140,7 @@ void register_node_tree_type_tex(void) tt->type = NTREE_TEXTURE; strcpy(tt->idname, "TextureNodeTree"); + strcpy(tt->group_idname, "TextureNodeGroup"); strcpy(tt->ui_name, N_("Texture Node Editor")); tt->ui_icon = ICON_NODE_TEXTURE; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Texture nodes")); diff --git a/source/blender/python/gpu/gpu_py_framebuffer.c b/source/blender/python/gpu/gpu_py_framebuffer.c index 33d9ff0b041..9bb2a9137f4 100644 --- a/source/blender/python/gpu/gpu_py_framebuffer.c +++ b/source/blender/python/gpu/gpu_py_framebuffer.c @@ -686,7 +686,7 @@ static struct PyMethodDef pygpu_framebuffer__tp_methods[] = { PyDoc_STRVAR(pygpu_framebuffer__tp_doc, ".. class:: GPUFrameBuffer(depth_slot=None, color_slots=None)\n" "\n" - " This object gives access to framebuffer functionallities.\n" + " This object gives access to framebuffer functionalities.\n" " When a 'layer' is specified in a argument, a single layer of a 3D or array " "texture is attached to the frame-buffer.\n" " For cube map textures, layer is translated into a cube map face.\n" diff --git a/source/blender/python/gpu/gpu_py_platform.c b/source/blender/python/gpu/gpu_py_platform.c index 656024ae22c..b877e3ceb98 100644 --- a/source/blender/python/gpu/gpu_py_platform.c +++ b/source/blender/python/gpu/gpu_py_platform.c @@ -55,6 +55,34 @@ static PyObject *pygpu_platform_version_get(PyObject *UNUSED(self)) return PyUnicode_FromString(GPU_platform_version()); } +PyDoc_STRVAR( + pygpu_platform_device_type_get_doc, + ".. function:: device_type_get()\n" + "\n" + " Get GPU device type.\n" + "\n" + " :return: Device type ('APPLE', 'NVIDIA', 'AMD', 'INTEL', 'SOFTWARE', 'UNKNOWN').\n" + " :rtype: str\n"); +static PyObject *pygpu_platform_device_type_get(PyObject *UNUSED(self)) +{ + if (GPU_type_matches(GPU_DEVICE_APPLE, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("APPLE"); + } + if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("NVIDIA"); + } + if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("AMD"); + } + if (GPU_type_matches(GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("INTEL"); + } + if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_ANY, GPU_DRIVER_ANY)) { + return PyUnicode_FromString("SOFTWARE"); + } + return PyUnicode_FromString("UNKNOWN"); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -74,6 +102,10 @@ static struct PyMethodDef pygpu_platform__tp_methods[] = { (PyCFunction)pygpu_platform_version_get, METH_NOARGS, pygpu_platform_version_get_doc}, + {"device_type_get", + (PyCFunction)pygpu_platform_device_type_get, + METH_NOARGS, + pygpu_platform_device_type_get_doc}, {NULL, NULL, 0, NULL}, }; diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index ac050128a1d..ab2ff59a689 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -292,7 +292,7 @@ static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, Py const char *name = PyUnicode_AsUTF8(identifier); id = GPU_vertformat_attr_id_get(format, name); if (id == -1) { - PyErr_SetString(PyExc_ValueError, "Unknown attribute name"); + PyErr_Format(PyExc_ValueError, "Unknown attribute '%s'", name); return NULL; } } diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 71138134370..9d2516969cf 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -174,8 +174,8 @@ if(WITH_CODEC_SNDFILE) add_definitions(-DWITH_SNDFILE) endif() -if(WITH_COMPOSITOR) - add_definitions(-DWITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_CYCLES) diff --git a/source/blender/python/intern/bpy_app_build_options.c b/source/blender/python/intern/bpy_app_build_options.c index fe5111c37f2..a744f3fd4fa 100644 --- a/source/blender/python/intern/bpy_app_build_options.c +++ b/source/blender/python/intern/bpy_app_build_options.c @@ -18,7 +18,7 @@ static PyStructSequence_Field app_builtopts_info_fields[] = { {"codec_avi", NULL}, {"codec_ffmpeg", NULL}, {"codec_sndfile", NULL}, - {"compositor", NULL}, + {"compositor_cpu", NULL}, {"cycles", NULL}, {"cycles_osl", NULL}, {"freestyle", NULL}, @@ -104,7 +104,7 @@ static PyObject *make_builtopts_info(void) SetObjIncref(Py_False); #endif -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU SetObjIncref(Py_True); #else SetObjIncref(Py_False); diff --git a/source/blender/python/intern/bpy_interface_atexit.c b/source/blender/python/intern/bpy_interface_atexit.c index 1ba3ab6a40b..cba9bd59abf 100644 --- a/source/blender/python/intern/bpy_interface_atexit.c +++ b/source/blender/python/intern/bpy_interface_atexit.c @@ -32,7 +32,7 @@ static PyObject *func_bpy_atregister = NULL; /* borrowed reference, `atexit` hol static void atexit_func_call(const char *func_name, PyObject *atexit_func_arg) { - /* NOTE(campbell): no error checking, if any of these fail we'll get a crash + /* NOTE(@campbellbarton): no error checking, if any of these fail we'll get a crash * this is intended, but if its problematic it could be changed. */ PyObject *atexit_mod = PyImport_ImportModuleLevel("atexit", NULL, NULL, NULL, 0); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index d9c004fb6fa..179a0250688 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -780,7 +780,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) return ret; } -/* NOTE(campbell): Regarding comparison `__cmp__`: +/* NOTE(@campbellbarton): Regarding comparison `__cmp__`: * checking the 'ptr->data' matches works in almost all cases, * however there are a few RNA properties that are fake sub-structs and * share the pointer with the parent, in those cases this happens 'a.b == a' @@ -8254,7 +8254,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, continue; } - /* TODO(campbell): this is used for classmethod's too, + /* TODO(@campbellbarton): this is used for classmethod's too, * even though class methods should have 'FUNC_USE_SELF_TYPE' set, see Operator.poll for eg. * Keep this as-is since it's working, but we should be using * 'FUNC_USE_SELF_TYPE' for many functions. */ @@ -8345,7 +8345,7 @@ static int bpy_class_validate_recursive(PointerRNA *dummyptr, continue; } - /* TODO(campbell): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */ + /* TODO(@campbellbarton): Use Python3.7x _PyObject_LookupAttr(), also in the macro below. */ identifier = RNA_property_identifier(prop); item = PyObject_GetAttrString(py_class, identifier); diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 9ffe2879779..54497a6572f 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -760,8 +760,8 @@ void RE_bake_pixels_populate(Mesh *me, for (int a = 0; a < 3; a++) { const float *uv = mloopuv[lt->tri[a]].uv; - /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our - * intersection tests where a pixel gets in between 2 faces or the middle of a quad, + /* NOTE(@campbellbarton): workaround for pixel aligned UVs which are common and can screw + * up our intersection tests where a pixel gets in between 2 faces or the middle of a quad, * camera aligned quads also have this problem but they are less common. * Add a small offset to the UVs, fixes bug T18685. */ vec[a][0] = (uv[0] - bk_image->uv_offset[0]) * (float)bk_image->width - (0.5f + 0.001f); diff --git a/source/blender/render/intern/texture_margin.cc b/source/blender/render/intern/texture_margin.cc index 37ef9213615..92146155437 100644 --- a/source/blender/render/intern/texture_margin.cc +++ b/source/blender/render/intern/texture_margin.cc @@ -558,8 +558,8 @@ static void generate_margin(ImBuf *ibuf, for (int a = 0; a < 3; a++) { const float *uv = mloopuv[lt->tri[a]].uv; - /* NOTE(campbell): workaround for pixel aligned UVs which are common and can screw up our - * intersection tests where a pixel gets in between 2 faces or the middle of a quad, + /* NOTE(@campbellbarton): workaround for pixel aligned UVs which are common and can screw up + * our intersection tests where a pixel gets in between 2 faces or the middle of a quad, * camera aligned quads also have this problem but they are less common. * Add a small offset to the UVs, fixes bug T18685. */ vec[a][0] = (uv[0] - uv_offset[0]) * (float)ibuf->x - (0.5f + 0.001f); diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 7a36020cea5..c434638549c 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -26,6 +26,7 @@ set(INC ../../../intern/glew-mx ../../../intern/guardedalloc ../../../intern/memutil + ../bmesh # for writefile.c: dna_type_offsets.h ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern @@ -164,11 +165,11 @@ if(WIN32 OR APPLE) endif() endif() -if(WITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) list(APPEND LIB bf_compositor ) - add_definitions(-DWITH_COMPOSITOR) + add_definitions(-DWITH_COMPOSITOR_CPU) endif() if(WITH_XR_OPENXR) diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index 4ecadbc5685..e165cb6b4f8 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -1053,7 +1053,7 @@ void WM_gizmomaptype_group_unlink(bContext *C, WM_gizmomaptype_group_free(gzgt_ref); } - /* TODO(campbell): Gizmos may share key-maps, for now don't + /* TODO(@campbellbarton): Gizmos may share key-maps, for now don't * remove however we could flag them as temporary/owned by the gizmo. */ #if 0 /* NOTE: we may want to keep this key-map for editing. */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index f5974a2176b..9903b0e50fd 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -259,7 +259,7 @@ bool WM_gizmomap_minmax(const wmGizmoMap *gzmap, * \param poll: Polling function for excluding gizmos. * \param data: Custom data passed to \a poll * - * TODO(campbell): this uses unreliable order, + * TODO(@campbellbarton): this uses unreliable order, * best we use an iterator function instead of a hash. */ static GHash *WM_gizmomap_gizmo_hash_new(const bContext *C, @@ -430,9 +430,9 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas return; } - /* TODO(campbell): This will need it own shader probably? + /* TODO(@campbellbarton): This will need it own shader probably? * Don't think it can be handled from that point though. */ - /* const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; */ + // const bool use_lighting = (U.gizmo_flag & V3D_GIZMO_SHADED) != 0; bool is_depth_prev = false; @@ -501,7 +501,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C, bool *r_use_select_bias) { - /* TODO(campbell): this depends on depth buffer being written to, + /* TODO(@campbellbarton): this depends on depth buffer being written to, * currently broken for the 3D view. */ bool is_depth_prev = false; bool is_depth_skip_prev = false; @@ -674,7 +674,7 @@ static wmGizmo *gizmo_find_intersected_3d(bContext *C, * This way we always use the first hit. */ if (has_3d) { - /* The depth buffer is needed for for gizmos to obscure each other. */ + /* The depth buffer is needed for gizmos to obscure each other. */ GPUViewport *viewport = WM_draw_region_get_viewport(CTX_wm_region(C)); /* When switching between modes and the mouse pointer is over a gizmo, the highlight test is diff --git a/source/blender/windowmanager/intern/wm_event_system.cc b/source/blender/windowmanager/intern/wm_event_system.cc index 5e7fe4678f6..77f6b3c861f 100644 --- a/source/blender/windowmanager/intern/wm_event_system.cc +++ b/source/blender/windowmanager/intern/wm_event_system.cc @@ -5174,7 +5174,7 @@ static bool wm_event_is_ignorable_key_press(const wmWindow *win, const wmEvent & return false; } - const wmEvent &last_event = *reinterpret_cast<const wmEvent *>(win->event_queue.last); + const wmEvent &last_event = *static_cast<const wmEvent *>(win->event_queue.last); return wm_event_is_same_key_press(last_event, event); } @@ -5214,6 +5214,13 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void event.prev_type = event.type; event.prev_val = event.val; + /* Always use modifiers from the active window since + changes to modifiers aren't sent to inactive windows, see: T66088. */ + if ((wm->winactive != win) && (wm->winactive && wm->winactive->eventstate)) { + event.modifier = wm->winactive->eventstate->modifier; + event.keymodifier = wm->winactive->eventstate->keymodifier; + } + /* Ensure the event state is correct, any deviation from this may cause bugs. * * NOTE: #EVENT_NONE is set when unknown keys are pressed, @@ -5256,6 +5263,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void if (win_other) { wmEvent event_other = *win_other->eventstate; + /* Use the modifier state of this window. */ + event_other.modifier = event.modifier; + event_other.keymodifier = event.keymodifier; + /* See comment for this operation on `event` for details. */ event_other.prev_type = event_other.type; event_other.prev_val = event_other.val; @@ -5345,6 +5356,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void if (win_other) { wmEvent event_other = *win_other->eventstate; + /* Use the modifier state of this window. */ + event_other.modifier = event.modifier; + event_other.keymodifier = event.keymodifier; + /* See comment for this operation on `event` for details. */ event_other.prev_type = event_other.type; event_other.prev_val = event_other.val; diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 45e8f8786df..1819ed13be3 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1887,9 +1887,6 @@ static void wm_autosave_location(char *filepath) { const int pid = abs(getpid()); char path[1024]; -#ifdef WIN32 - const char *savedir; -#endif /* Normally there is no need to check for this to be NULL, * however this runs on exit when it may be cleared. */ @@ -1915,7 +1912,7 @@ static void wm_autosave_location(char *filepath) * through BLI_windows_get_default_root_dir(). * If there is no C:\tmp autosave fails. */ if (!BLI_exists(BKE_tempdir_base())) { - savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL); + const char *savedir = BKE_appdir_folder_id_create(BLENDER_USER_AUTOSAVE, NULL); BLI_make_file_string("/", filepath, savedir, path); return; } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 7f5ec77e16d..624e434e784 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -540,7 +540,7 @@ void WM_exit_ex(bContext *C, const bool do_python) BKE_vfont_clipboard_free(); BKE_node_clipboard_free(); -#ifdef WITH_COMPOSITOR +#ifdef WITH_COMPOSITOR_CPU COM_deinitialize(); #endif diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index a1ebe1fc76f..0c31ff87fdd 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -1113,14 +1113,22 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt } wmWindow *win = GHOST_GetWindowUserData(ghostwin); + /* Win23/GHOST modifier bug, see T40317 */ +#ifndef WIN32 +//# define USE_WIN_ACTIVATE +#endif + switch (type) { case GHOST_kEventWindowDeactivate: wm_event_add_ghostevent(wm, win, type, data); win->active = 0; /* XXX */ - /* clear modifiers for inactive windows */ + /* When window activation is enabled, these modifiers are set with window activation. + * Otherwise leave them set so re-activation doesn't loose keys which are held. */ +#ifdef USE_WIN_ACTIVATE win->eventstate->modifier = 0; win->eventstate->keymodifier = 0; +#endif break; case GHOST_kEventWindowActivate: { @@ -1128,11 +1136,6 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt (query_qual(CONTROL) ? KM_CTRL : 0) | (query_qual(ALT) ? KM_ALT : 0) | (query_qual(OS) ? KM_OSKEY : 0)); - /* Win23/GHOST modifier bug, see T40317 */ -#ifndef WIN32 -//# define USE_WIN_ACTIVATE -#endif - /* No context change! C->wm->windrawable is drawable, or for area queues. */ wm->winactive = win; @@ -1367,6 +1370,10 @@ static bool ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_pt event.xy[0] = ddd->x; event.xy[1] = ddd->y; + /* The values from #wm_window_update_eventstate may not match (under WAYLAND they don't) + * Write this into the event state. */ + copy_v2_v2_int(win->eventstate->xy, event.xy); + event.flag = 0; /* No context change! C->wm->windrawable is drawable, or for area queues. */ diff --git a/source/blender/windowmanager/message_bus/wm_message_bus.h b/source/blender/windowmanager/message_bus/wm_message_bus.h index 1bc983f20ad..1558fe4004e 100644 --- a/source/blender/windowmanager/message_bus/wm_message_bus.h +++ b/source/blender/windowmanager/message_bus/wm_message_bus.h @@ -66,7 +66,7 @@ typedef struct wmMsg { } wmMsg; typedef struct wmMsgSubscribeKey { - /** Linked list for predicable ordering, otherwise we would depend on #GHash bucketing. */ + /** Linked list for predictable ordering, otherwise we would depend on #GHash bucketing. */ struct wmMsgSubscribeKey *next, *prev; ListBase values; /* over-alloc, eg: wmMsgSubscribeKey_RNA */ diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 11f48a72908..b9912929a54 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -200,8 +200,10 @@ if(WITH_BUILDINFO) SET_SOURCE_FILES_PROPERTIES(${buildinfo_h_fake} PROPERTIES SYMBOLIC TRUE) # a custom target that is always built - add_custom_target(buildinfo ALL - DEPENDS ${buildinfo_h_fake}) + add_custom_target( + buildinfo ALL + DEPENDS ${buildinfo_h_fake} + ) # creates buildinfo.h using cmake script add_custom_command( @@ -272,13 +274,13 @@ if(WITH_PYTHON_MODULE) else() add_executable(blender ${EXETYPE} ${SRC}) if(WIN32) - add_executable(blender-launcher WIN32 - blender_launcher_win32.c - ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc - ${CMAKE_BINARY_DIR}/blender.exe.manifest - ) - target_compile_definitions (blender-launcher PRIVATE -D_UNICODE -DUNICODE) - target_link_libraries(blender-launcher Pathcch.lib) + add_executable(blender-launcher WIN32 + blender_launcher_win32.c + ${CMAKE_SOURCE_DIR}/release/windows/icons/winblender.rc + ${CMAKE_BINARY_DIR}/blender.exe.manifest + ) + target_compile_definitions (blender-launcher PRIVATE -D_UNICODE -DUNICODE) + target_link_libraries(blender-launcher Pathcch.lib) endif() endif() @@ -405,8 +407,8 @@ if(WITH_INTERNATIONAL) # Create a custom target which will compile all po to mo add_custom_target( locales - DEPENDS ${_all_mo_files}) - + DEPENDS ${_all_mo_files} + ) add_dependencies(blender locales) # Generate INSTALL rules @@ -464,7 +466,8 @@ if(UNIX AND NOT APPLE) blender_man_page ALL COMMAND ${CMAKE_SOURCE_DIR}/doc/manpage/blender.1.py --blender ${EXECUTABLE_OUTPUT_PATH}/blender - --output ${CMAKE_CURRENT_BINARY_DIR}/blender.1) + --output ${CMAKE_CURRENT_BINARY_DIR}/blender.1 + ) add_dependencies(blender_man_page blender) endif() endif() @@ -719,15 +722,15 @@ elseif(WIN32) if(WITH_OPENMP AND MSVC_CLANG) install( - FILES ${CLANG_OPENMP_DLL} - DESTINATION "." + FILES ${CLANG_OPENMP_DLL} + DESTINATION "." ) endif() if(WITH_FFTW3) install( - FILES ${LIBDIR}/fftw3/lib/libfftw3-3.dll - DESTINATION "." + FILES ${LIBDIR}/fftw3/lib/libfftw3-3.dll + DESTINATION "." ) endif() if(MSVC_ASAN) @@ -738,14 +741,14 @@ elseif(WIN32) message(FATAL_ERROR "Asan is enabled, but the ASAN runtime is not detected, this is an optional component during the MSVC install, please install it") endif() install( - FILES ${ASAN_DLL} - DESTINATION "." - CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + FILES ${ASAN_DLL} + DESTINATION "." + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( - FILES ${ASAN_DEBUG_DLL} - DESTINATION "." - CONFIGURATIONS Debug + FILES ${ASAN_DEBUG_DLL} + DESTINATION "." + CONFIGURATIONS Debug ) unset(ASAN_DLL) unset(ASAN_DEBUG_DLL) @@ -753,18 +756,18 @@ elseif(WIN32) if(WITH_GMP) install( - FILES ${LIBDIR}/gmp/lib/libgmp-10.dll - DESTINATION "." + FILES ${LIBDIR}/gmp/lib/libgmp-10.dll + DESTINATION "." ) install( - FILES ${LIBDIR}/gmp/lib/libgmpxx.dll - DESTINATION "." - CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + FILES ${LIBDIR}/gmp/lib/libgmpxx.dll + DESTINATION "." + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel ) install( - FILES ${LIBDIR}/gmp/lib/libgmpxx_d.dll - DESTINATION "." - CONFIGURATIONS Debug + FILES ${LIBDIR}/gmp/lib/libgmpxx_d.dll + DESTINATION "." + CONFIGURATIONS Debug ) endif() @@ -780,16 +783,16 @@ elseif(WIN32) endif() if(WITH_OPENVDB) - install( - FILES ${LIBDIR}/openvdb/bin/openvdb.dll - DESTINATION "." - CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel - ) - install( - FILES ${LIBDIR}/openvdb/bin/openvdb_d.dll - DESTINATION "." - CONFIGURATIONS Debug - ) + install( + FILES ${LIBDIR}/openvdb/bin/openvdb.dll + DESTINATION "." + CONFIGURATIONS Release;RelWithDebInfo;MinSizeRel + ) + install( + FILES ${LIBDIR}/openvdb/bin/openvdb_d.dll + DESTINATION "." + CONFIGURATIONS Debug + ) endif() if(WITH_PYTHON) @@ -1054,7 +1057,8 @@ elseif(APPLE) set_target_properties(blender PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${OSX_APP_SOURCEDIR}/Contents/Info.plist MACOSX_BUNDLE_SHORT_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH}" - MACOSX_BUNDLE_LONG_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH} ${BLENDER_DATE}") + MACOSX_BUNDLE_LONG_VERSION_STRING "${BLENDER_VERSION}.${BLENDER_VERSION_PATCH} ${BLENDER_DATE}" + ) # Gather the date in finder-style execute_process(COMMAND date "+%m/%d/%Y/%H:%M" @@ -1151,9 +1155,10 @@ endif() if(DEFINED BLENDER_TEXT_FILES_DESTINATION) - configure_file(${CMAKE_SOURCE_DIR}/release/text/readme.html - ${CMAKE_BINARY_DIR}/release/text/readme.html - @ONLY + configure_file( + ${CMAKE_SOURCE_DIR}/release/text/readme.html + ${CMAKE_BINARY_DIR}/release/text/readme.html + @ONLY ) list(APPEND BLENDER_TEXT_FILES ${CMAKE_BINARY_DIR}/release/text/readme.html @@ -1240,17 +1245,18 @@ if(WIN32) set_target_properties(blender PROPERTIES VS_GLOBAL_VcpkgEnabled "false") set_target_properties(blender PROPERTIES PDB_NAME "blender_private" - PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>") - if(WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB) - # This is slightly messy, but single target generators like ninja will not have the - # CMAKE_CFG_INTDIR variable and multitarget generators like msbuild will not have - # CMAKE_BUILD_TYPE. This can be simplified by target_link_options and the $<CONFIG> - # generator expression in newer cmake (2.13+) but until that time this fill have suffice. - if(CMAKE_BUILD_TYPE) - set_property(TARGET blender APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/blender_public.pdb") - else() - set_property(TARGET blender APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/blender_public.pdb") - endif() + PDB_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>" + ) + if(WITH_WINDOWS_PDB AND WITH_WINDOWS_STRIPPED_PDB) + # This is slightly messy, but single target generators like ninja will not have the + # `CMAKE_CFG_INTDIR` variable and multi-target generators like `msbuild` will not have + # `CMAKE_BUILD_TYPE`. This can be simplified by target_link_options and the `$<CONFIG>` + # generator expression in newer CMAKE (2.13+) but until that time this fill have suffice. + if(CMAKE_BUILD_TYPE) + set_property(TARGET blender APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}/blender_public.pdb") + else() + set_property(TARGET blender APPEND_STRING PROPERTY LINK_FLAGS " /PDBSTRIPPED:${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}/blender_public.pdb") + endif() endif() endif() diff --git a/source/creator/creator.c b/source/creator/creator.c index 6c95ee3e490..e7a803d383f 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -41,7 +41,6 @@ #include "BKE_global.h" #include "BKE_gpencil_modifier.h" #include "BKE_idtype.h" -#include "BKE_image.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_modifier.h" diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py index 6c9ba70141f..d95d386569e 100644 --- a/tests/performance/api/graph.py +++ b/tests/performance/api/graph.py @@ -74,7 +74,7 @@ class TestGraph: revisions[revision] = len(revisions) revision_dates[revision] = int(entry.date) - # Google Charts JSON data layout is like a spreadsheat table, with + # Google Charts JSON data layout is like a spreadsheet table, with # columns, rows, and cells. We create one column for revision labels, # and one column for each test. cols = [] diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index d95f2cd2644..ca3070b60ad 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -635,8 +635,8 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS) MESSAGE(WARNING "Disabling render tests because OIIO idiff does not exist") elseif(NOT EXISTS "${TEST_SRC_DIR}/render/shader") MESSAGE(WARNING "Disabling render tests because tests folder does not exist at ${TEST_SRC_DIR}") - elseif(NOT WITH_COMPOSITOR) - MESSAGE(WARNING "Disabling render tests because WITH_COMPOSITOR is disabled") + elseif(NOT WITH_COMPOSITOR_CPU) + MESSAGE(WARNING "Disabling render tests because WITH_COMPOSITOR_CPU is disabled") elseif(NOT WITH_OPENCOLORIO) MESSAGE(WARNING "Disabling render tests because WITH_OPENCOLORIO is disabled") else() @@ -735,7 +735,7 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS) endif() endif() -if(WITH_COMPOSITOR) +if(WITH_COMPOSITOR_CPU) set(compositor_tests color converter diff --git a/tests/python/bl_run_operators.py b/tests/python/bl_run_operators.py index a2478bd7547..ccb0814e5eb 100644 --- a/tests/python/bl_run_operators.py +++ b/tests/python/bl_run_operators.py @@ -317,7 +317,6 @@ def ctx_editmode_mesh_extra(): bpy.ops.object.shape_key_add(from_mix=False) bpy.ops.object.shape_key_add(from_mix=True) bpy.ops.mesh.uv_texture_add() - bpy.ops.mesh.vertex_color_add() bpy.ops.object.material_slot_add() # editmode last! bpy.ops.object.mode_set(mode='EDIT') diff --git a/tests/python/eevee_render_tests.py b/tests/python/eevee_render_tests.py index 68895291044..9ed850fcb52 100644 --- a/tests/python/eevee_render_tests.py +++ b/tests/python/eevee_render_tests.py @@ -3,6 +3,7 @@ import argparse import os +import pathlib import shlex import shutil import subprocess @@ -99,6 +100,26 @@ if inside_blender: sys.exit(1) +def get_gpu_device_type(blender): + command = [ + blender, + "-noaudio", + "--background" + "--factory-startup", + "--python", + str(pathlib.Path(__file__).parent / "gpu_info.py") + ] + try: + completed_process = subprocess.run(command, stdout=subprocess.PIPE) + for line in completed_process.stdout.read_text(): + if line.startswith("GPU_DEVICE_TYPE:"): + vendor = line.split(':')[1] + return vendor + except BaseException as e: + return None + return None + + def get_arguments(filepath, output_filepath): return [ "--background", @@ -134,10 +155,16 @@ def main(): idiff = args.idiff[0] output_dir = args.outdir[0] + gpu_device_type = get_gpu_device_type(blender) + reference_override_dir = None + if gpu_device_type == "AMD": + reference_override_dir = "eevee_renders/amd" + from modules import render_report report = render_report.Report("Eevee", output_dir, idiff) report.set_pixelated(True) report.set_reference_dir("eevee_renders") + report.set_reference_override_dir(reference_override_dir) report.set_compare_engine('cycles', 'CPU') test_dir_name = Path(test_dir).name diff --git a/tests/python/gpu_info.py b/tests/python/gpu_info.py new file mode 100644 index 00000000000..426ce29e85d --- /dev/null +++ b/tests/python/gpu_info.py @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +""" +Prints GPU back-end information to the console and exits. + +Use this script as `blender --background --python gpu_info.py`. +""" +import bpy +import gpu +import sys + +# Render with workbench to initialize the GPU backend otherwise it would fail when running in +# background mode as the GPU backend won't be initialized. +scene = bpy.context.scene +scene.render.resolution_x = 1 +scene.render.resolution_y = 1 +scene.render.engine = "BLENDER_WORKBENCH" +bpy.ops.render.render(animation=False, write_still=False) + + +print('GPU_VENDOR:' + gpu.platform.vendor_get()) +print('GPU_RENDERER:' + gpu.platform.renderer_get()) +print('GPU_VERSION:' + gpu.platform.version_get()) +print('GPU_DEVICE_TYPE:' + gpu.platform.device_type_get()) + +sys.exit(0) diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index 15441918800..15d46d6d127 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -78,12 +78,18 @@ def test_get_name(filepath): return os.path.splitext(filename)[0] -def test_get_images(output_dir, filepath, reference_dir): +def test_get_images(output_dir, filepath, reference_dir, reference_override_dir): testname = test_get_name(filepath) dirpath = os.path.dirname(filepath) old_dirpath = os.path.join(dirpath, reference_dir) old_img = os.path.join(old_dirpath, testname + ".png") + if reference_override_dir: + override_dirpath = os.path.join(dirpath, reference_override_dir) + override_img = os.path.join(override_dirpath, testname + ".png") + if os.path.exists(override_img): + old_dirpath = override_dirpath + old_img = override_img ref_dirpath = os.path.join(output_dir, os.path.basename(dirpath), "ref") ref_img = os.path.join(ref_dirpath, testname + ".png") @@ -108,6 +114,7 @@ class Report: 'output_dir', 'global_dir', 'reference_dir', + 'reference_override_dir', 'idiff', 'pixelated', 'fail_threshold', @@ -127,6 +134,7 @@ class Report: self.output_dir = output_dir self.global_dir = os.path.dirname(output_dir) self.reference_dir = 'reference_renders' + self.reference_override_dir = None self.idiff = idiff self.compare_engine = None self.fail_threshold = 0.016 @@ -161,6 +169,9 @@ class Report: def set_reference_dir(self, reference_dir): self.reference_dir = reference_dir + def set_reference_override_dir(self, reference_override_dir): + self.reference_override_dir = reference_override_dir + def set_compare_engine(self, other_engine, other_device=None): self.compare_engine = (other_engine, other_device) @@ -343,7 +354,8 @@ class Report: name = test_get_name(filepath) name = name.replace('_', ' ') - old_img, ref_img, new_img, diff_img = test_get_images(self.output_dir, filepath, self.reference_dir) + old_img, ref_img, new_img, diff_img = test_get_images( + self.output_dir, filepath, self.reference_dir, self.reference_override_dir) status = error if error else "" tr_style = """ class="table-danger" """ if error else "" @@ -390,7 +402,8 @@ class Report: self.compare_tests += test_html def _diff_output(self, filepath, tmp_filepath): - old_img, ref_img, new_img, diff_img = test_get_images(self.output_dir, filepath, self.reference_dir) + old_img, ref_img, new_img, diff_img = test_get_images( + self.output_dir, filepath, self.reference_dir, self.reference_override_dir) # Create reference render directory. old_dirpath = os.path.dirname(old_img) diff --git a/tests/python/operators.py b/tests/python/operators.py index 548a2b50b05..fc2e8e39d4f 100644 --- a/tests/python/operators.py +++ b/tests/python/operators.py @@ -115,6 +115,18 @@ def main(): [OperatorSpecEditMode("dissolve_faces", {}, "VERT", {5, 34, 47, 49, 83, 91, 95})], ), + # dissolve limited + SpecMeshTest( + "SphereDissolveLimited", "testSphereDissolveLimited", "expectedSphereDissolveLimited", + [OperatorSpecEditMode("dissolve_limited", {"angle_limit": 0.610865}, "FACE", {20, 23, 26, 29, 32})], + ), + + # dissolve mode + SpecMeshTest( + "PlaneDissolveMode", "testPlaneDissolveMode", "expectedPlaneDissolveMode", + [OperatorSpecEditMode("dissolve_mode", {"use_verts": True}, "FACE", {0, 1, 2, 10, 12, 13})], + ), + # dissolve verts SpecMeshTest( "CubeDissolveVerts", "testCubeDissolveVerts", "expectedCubeDissolveVerts", @@ -332,6 +344,12 @@ def main(): [OperatorSpecEditMode("mark_seam", {}, "EDGE", {1})], ), + # merge normals + SpecMeshTest( + "CubeMergeNormals", "testCubeMergeNormals", "expectedCubeMergeNormals", + [OperatorSpecEditMode("merge_normals", {}, "FACE", {3, 5})], + ), + # select all SpecMeshTest( "CircleSelectAll", "testCircleSelectAll", "expectedCircleSelectAll", @@ -545,24 +563,6 @@ def main(): )], ), - # Vertex Colors - SpecMeshTest( - "VertexColorAdd", "testCubeColorAdd", "expectedCubeColorAdd", - [OperatorSpecEditMode("vertex_color_add", {}, "VERT", {})], - ), - SpecMeshTest( - "VertexColorRemove", "testCubeColorRemove", "expectedCubeColorRemove", - [OperatorSpecEditMode("vertex_color_remove", {}, "VERT", {})], - ), - SpecMeshTest( - "VertexColorSculptAdd", "testCubeSculptAdd", "expectedCubeSculptAdd", - [OperatorSpecEditMode("sculpt_vertex_color_add", {}, "VERT", {})], - ), - SpecMeshTest( - "VertexColorSculptRemove", "testCubeSculptRemove", "expectedCubeSculptRemove", - [OperatorSpecEditMode("sculpt_vertex_color_remove", {}, "VERT", {})], - ), - # Laplacian Smooth SpecMeshTest( "LaplacianSmoothDefault", "testSphereLaplacianSmoothDefault", "expectedSphereLaplacianSmoothDefault", |