diff options
author | mattoverby <mattoverby@gmail.com> | 2020-08-28 23:00:01 +0300 |
---|---|---|
committer | mattoverby <mattoverby@gmail.com> | 2020-08-28 23:00:01 +0300 |
commit | 44819c69a4e702b224c4ba166def9e212bee6d92 (patch) | |
tree | 45f0349bd9ad29892d14b0f63d496b309cd32ed8 | |
parent | 6c32148cd2ea90cde4d4e46beb5ad4969b044c49 (diff) | |
parent | 019cd2e56b377a35b1fa2c85aebe60fb8c495335 (diff) |
fixed submodulessoc-2020-soft-body
536 files changed, 28935 insertions, 17601 deletions
diff --git a/.clang-format b/.clang-format index 4db1b664fdb..8a992fea3a9 100644 --- a/.clang-format +++ b/.clang-format @@ -253,8 +253,8 @@ ForEachMacros: - RNA_STRUCT_BEGIN_SKIP_RNA_TYPE - SCULPT_VERTEX_DUPLICATES_AND_NEIGHBORS_ITER_BEGIN - SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN - - SEQP_BEGIN - - SEQ_BEGIN + - SEQ_ALL_BEGIN + - SEQ_CURRENT_BEGIN - SURFACE_QUAD_ITER_BEGIN - foreach - ED_screen_areas_iter diff --git a/CMakeLists.txt b/CMakeLists.txt index cb1c7208c14..e8df9386a4e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,11 @@ if(POLICY CMP0068) cmake_policy(SET CMP0068 NEW) endif() +# find_package() uses <PackageName>_ROOT variables. +if(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +endif() + #----------------------------------------------------------------------------- # Load some macros. include(build_files/cmake/macros.cmake) @@ -183,6 +188,7 @@ if(APPLE) else() option(WITH_XR_OPENXR "Enable VR features through the OpenXR specification" ON) endif() +option(WITH_GMP "Enable features depending on GMP (Exact Boolean)" ON) # Compositor option(WITH_COMPOSITOR "Enable the tile based nodal compositor" ON) @@ -1569,6 +1575,12 @@ endif() if(MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /std:c++17") + # Make MSVC properly report the value of the __cplusplus preprocessor macro + # Available MSVC 15.7 (1914) and up, without this it reports 199711L regardless + # of the C++ standard chosen above + if(MSVC_VERSION GREATER 1913) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /Zc:__cplusplus") + endif() elseif( CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang" OR @@ -1732,6 +1744,7 @@ if(FIRST_RUN) info_cfg_option(WITH_TETGEN) info_cfg_option(WITH_USD) info_cfg_option(WITH_TBB) + info_cfg_option(WITH_GMP) info_cfg_text("Compiler Options:") info_cfg_option(WITH_BUILDINFO) diff --git a/build_files/build_environment/CMakeLists.txt b/build_files/build_environment/CMakeLists.txt index 6415d270773..6368430235c 100644 --- a/build_files/build_environment/CMakeLists.txt +++ b/build_files/build_environment/CMakeLists.txt @@ -120,6 +120,7 @@ endif() if(NOT WIN32 OR ENABLE_MINGW64) include(cmake/gmp.cmake) include(cmake/openjpeg.cmake) + include(cmake/gmp.cmake) if(NOT WIN32 OR BUILD_MODE STREQUAL Release) if(WIN32) include(cmake/zlib_mingw.cmake) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index 30a0b1184c2..eb5df8204b5 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -312,6 +312,7 @@ set(NASM_HASH f4fd1329b1713e1ccd34b2fc121c4bcd278c9f91cc4cb205ae8fcd2e4728dd14) set(XR_OPENXR_SDK_VERSION 1.0.8) set(XR_OPENXR_SDK_URI https://github.com/KhronosGroup/OpenXR-SDK/archive/release-${XR_OPENXR_SDK_VERSION}.tar.gz) set(XR_OPENXR_SDK_HASH c6de63d2e0f9029aa58dfa97cad8ce07) + set(ISPC_VERSION v1.13.0) set(ISPC_URI https://github.com/ispc/ispc/archive/${ISPC_VERSION}.tar.gz) set(ISPC_HASH 4bf5e8d0020c4b9980faa702c1a6f25f) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index 130173d7f01..43606f49c78 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -1087,7 +1087,7 @@ Those libraries should be available as packages in all recent distributions (opt * libjpeg, libpng, libtiff, [openjpeg2], [libopenal]. * libx11, libxcursor, libxi, libxrandr, libxinerama (and other libx... as needed). * libsqlite3, libbz2, libssl, libfftw3, libxml2, libtinyxml, yasm, libyaml-cpp. - * libsdl2, libglew, [libgmp], [libglewmx].\"" + * libsdl2, libglew, [libgmp], [libglewmx], fontconfig.\"" DEPS_SPECIFIC_INFO="\"BUILDABLE DEPENDENCIES: @@ -3654,7 +3654,7 @@ install_DEB() { THEORA_DEV="libtheora-dev" _packages="gawk cmake cmake-curses-gui build-essential libjpeg-dev libpng-dev libtiff-dev \ - git libfreetype6-dev libx11-dev flex bison libxxf86vm-dev \ + git libfreetype6-dev libfontconfig-dev libx11-dev flex bison libxxf86vm-dev \ libxcursor-dev libxi-dev wget libsqlite3-dev libxrandr-dev libxinerama-dev \ libbz2-dev libncurses5-dev libssl-dev liblzma-dev libreadline-dev \ libopenal-dev libglew-dev yasm $THEORA_DEV $VORBIS_DEV $OGG_DEV \ @@ -4320,7 +4320,7 @@ install_RPM() { OGG_DEV="libogg-devel" THEORA_DEV="libtheora-devel" - _packages="gcc gcc-c++ git make cmake tar bzip2 xz findutils flex bison \ + _packages="gcc gcc-c++ git make cmake tar bzip2 xz findutils flex bison fontconfig-devel \ libtiff-devel libjpeg-devel libpng-devel sqlite-devel fftw-devel SDL2-devel \ libX11-devel libXi-devel libXcursor-devel libXrandr-devel libXinerama-devel \ wget ncurses-devel readline-devel $OPENJPEG_DEV openal-soft-devel \ @@ -4898,7 +4898,7 @@ install_ARCH() { BASE_DEVEL=`pacman -Sgq base-devel | sed -e 's/^gcc$/gcc-multilib/g' | paste -s -d' '` fi - _packages="$BASE_DEVEL git cmake \ + _packages="$BASE_DEVEL git cmake fontconfig \ libxi libxcursor libxrandr libxinerama glew libpng libtiff wget openal \ $OPENJPEG_DEV $VORBIS_DEV $OGG_DEV $THEORA_DEV yasm sdl2 fftw \ libxml2 yaml-cpp tinyxml python-requests jemalloc gmp" @@ -5577,7 +5577,7 @@ print_info() { _buildargs="-U *SNDFILE* -U PYTHON* -U *BOOST* -U *Boost* -U *TBB*" _buildargs="$_buildargs -U *OPENCOLORIO* -U *OPENEXR* -U *OPENIMAGEIO* -U *LLVM* -U *CYCLES*" - _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC* -U *USD*" + _buildargs="$_buildargs -U *OPENSUBDIV* -U *OPENVDB* -U *BLOSC* -U *COLLADA* -U *FFMPEG* -U *ALEMBIC* -U *USD*" _buildargs="$_buildargs -U *EMBREE* -U *OPENIMAGEDENOISE* -U *OPENXR*" _1="-D WITH_CODEC_SNDFILE=ON" diff --git a/build_files/cmake/Modules/FindEmbree.cmake b/build_files/cmake/Modules/FindEmbree.cmake index ccd0d6cd40a..2b3cd8e20c4 100644 --- a/build_files/cmake/Modules/FindEmbree.cmake +++ b/build_files/cmake/Modules/FindEmbree.cmake @@ -59,6 +59,14 @@ FOREACH(COMPONENT ${_embree_FIND_COMPONENTS}) PATH_SUFFIXES lib64 lib ) + IF (NOT EMBREE_${UPPERCOMPONENT}_LIBRARY) + IF (EMBREE_EMBREE3_LIBRARY) + # If we can't find all the static libraries, try to fall back to the shared library if found. + # This allows building with a shared embree library + SET(_embree_LIBRARIES ${EMBREE_EMBREE3_LIBRARY}) + BREAK() + ENDIF () + ENDIF () LIST(APPEND _embree_LIBRARIES "${EMBREE_${UPPERCOMPONENT}_LIBRARY}") ENDFOREACH() diff --git a/build_files/cmake/Modules/GTestTesting.cmake b/build_files/cmake/Modules/GTestTesting.cmake index a744f4202da..053d5196f41 100644 --- a/build_files/cmake/Modules/GTestTesting.cmake +++ b/build_files/cmake/Modules/GTestTesting.cmake @@ -70,6 +70,9 @@ macro(BLENDER_SRC_GTEST_EX) if(WITH_TBB) target_link_libraries(${TARGET_NAME} ${TBB_LIBRARIES}) endif() + if(WITH_GMP) + target_link_libraries(${TARGET_NAME} ${GMP_LIBRARIES}) + endif() get_property(GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(GENERATOR_IS_MULTI_CONFIG) diff --git a/build_files/cmake/config/blender_full.cmake b/build_files/cmake/config/blender_full.cmake index 7d3284af158..0f27ff61e8b 100644 --- a/build_files/cmake/config/blender_full.cmake +++ b/build_files/cmake/config/blender_full.cmake @@ -15,11 +15,12 @@ set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE) set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE) set(WITH_DRACO ON CACHE BOOL "" FORCE) set(WITH_FFTW3 ON CACHE BOOL "" FORCE) -set(WITH_GMP OFF CACHE BOOL "" FORCE) +set(WITH_GMP ON CACHE BOOL "" FORCE) set(WITH_LIBMV ON CACHE BOOL "" FORCE) set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE) set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE) set(WITH_FREESTYLE ON CACHE BOOL "" FORCE) +set(WITH_GMP ON CACHE BOOL "" FORCE) set(WITH_IK_SOLVER ON CACHE BOOL "" FORCE) set(WITH_IK_ITASC ON CACHE BOOL "" FORCE) set(WITH_IMAGE_CINEON ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 16c15961c59..7a664bbc008 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -25,6 +25,7 @@ set(WITH_LIBMV OFF CACHE BOOL "" FORCE) set(WITH_LLVM OFF CACHE BOOL "" FORCE) set(WITH_COMPOSITOR OFF CACHE BOOL "" FORCE) set(WITH_FREESTYLE OFF CACHE BOOL "" FORCE) +set(WITH_GMP OFF CACHE BOOL "" FORCE) set(WITH_IK_SOLVER OFF CACHE BOOL "" FORCE) set(WITH_IK_ITASC OFF CACHE BOOL "" FORCE) set(WITH_IMAGE_CINEON OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/config/blender_release.cmake b/build_files/cmake/config/blender_release.cmake index ddd9aa1d766..93962d7ebb0 100644 --- a/build_files/cmake/config/blender_release.cmake +++ b/build_files/cmake/config/blender_release.cmake @@ -16,11 +16,12 @@ set(WITH_CYCLES_EMBREE ON CACHE BOOL "" FORCE) set(WITH_CYCLES_OSL ON CACHE BOOL "" FORCE) set(WITH_DRACO ON CACHE BOOL "" FORCE) set(WITH_FFTW3 ON CACHE BOOL "" FORCE) -set(WITH_GMP OFF CACHE BOOL "" FORCE) +set(WITH_GMP ON CACHE BOOL "" FORCE) set(WITH_LIBMV ON CACHE BOOL "" FORCE) set(WITH_LIBMV_SCHUR_SPECIALIZATIONS ON CACHE BOOL "" FORCE) set(WITH_COMPOSITOR ON CACHE BOOL "" FORCE) set(WITH_FREESTYLE ON CACHE BOOL "" FORCE) +set(WITH_GMP ON CACHE BOOL "" FORCE) set(WITH_IK_SOLVER ON CACHE BOOL "" FORCE) set(WITH_IK_ITASC ON CACHE BOOL "" FORCE) set(WITH_IMAGE_CINEON ON CACHE BOOL "" FORCE) diff --git a/build_files/cmake/macros.cmake b/build_files/cmake/macros.cmake index 51cfadecc3e..dcab6d58870 100644 --- a/build_files/cmake/macros.cmake +++ b/build_files/cmake/macros.cmake @@ -496,6 +496,10 @@ function(SETUP_LIBDIRS) link_directories(${ALEMBIC_LIBPATH}) endif() + if(WITH_GMP) + link_directories(${GMP_LIBPATH}) + endif() + if(WITH_GHOST_WAYLAND) link_directories( ${wayland-client_LIBRARY_DIRS} diff --git a/build_files/cmake/platform/platform_apple.cmake b/build_files/cmake/platform/platform_apple.cmake index ace5de3330c..822110cb88f 100644 --- a/build_files/cmake/platform/platform_apple.cmake +++ b/build_files/cmake/platform/platform_apple.cmake @@ -407,6 +407,15 @@ if(WITH_TBB) find_package(TBB) endif() +if(WITH_GMP) + find_package(GMP) + + if(NOT GMP_FOUND) + set(WITH_GMP OFF) + message(STATUS "GMP not found") + endif() +endif() + # CMake FindOpenMP doesn't know about AppleClang before 3.12, so provide custom flags. if(WITH_OPENMP) if(CMAKE_C_COMPILER_ID MATCHES "AppleClang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER_EQUAL "7.0") diff --git a/build_files/cmake/platform/platform_unix.cmake b/build_files/cmake/platform/platform_unix.cmake index 83909a0cf66..3a7875ca46c 100644 --- a/build_files/cmake/platform/platform_unix.cmake +++ b/build_files/cmake/platform/platform_unix.cmake @@ -427,6 +427,15 @@ if(WITH_TBB) find_package_wrapper(TBB) endif() +if(WITH_GMP) + find_package(GMP) + + if(NOT GMP_FOUND) + set(WITH_GMP OFF) + message(STATUS "GMP not found") + endif() +endif() + if(WITH_XR_OPENXR) find_package(XR_OpenXR_SDK) if(NOT XR_OPENXR_SDK_FOUND) @@ -590,6 +599,14 @@ endif() if(CMAKE_COMPILER_IS_GNUCC) set(PLATFORM_CFLAGS "-pipe -fPIC -funsigned-char -fno-strict-aliasing") + # `maybe-uninitialized` is unreliable in release builds, but fine in debug builds. + set(GCC_EXTRA_FLAGS_RELEASE "-Wno-maybe-uninitialized") + set(CMAKE_C_FLAGS_RELEASE "${GCC_EXTRA_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELEASE}") + set(CMAKE_C_FLAGS_RELWITHDEBINFO "${GCC_EXTRA_FLAGS_RELEASE} ${CMAKE_C_FLAGS_RELWITHDEBINFO}") + set(CMAKE_CXX_FLAGS_RELEASE "${GCC_EXTRA_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELEASE}") + set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${GCC_EXTRA_FLAGS_RELEASE} ${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + unset(GCC_EXTRA_FLAGS_RELEASE) + if(WITH_LINKER_GOLD) execute_process( COMMAND ${CMAKE_C_COMPILER} -fuse-ld=gold -Wl,--version diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index b8af2dfc961..007f77a583f 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -136,6 +136,13 @@ add_definitions( # MSVC11 needs _ALLOW_KEYWORD_MACROS to build add_definitions(-D_ALLOW_KEYWORD_MACROS) +# RTTI is on by default even without this switch +# however having it in the CXX Flags makes it difficult +# to remove for individual files that want to disable it +# using the /GR- flag without generating a build warning +# that both /GR and /GR- are specified. +remove_cc_flag("/GR") + # We want to support Windows 7 level ABI add_definitions(-D_WIN32_WINNT=0x601) include(build_files/cmake/platform/platform_win32_bundle_crt.cmake) diff --git a/doc/python_api/rst/change_log.rst b/doc/python_api/rst/change_log.rst index f0384379499..957bf8605e3 100644 --- a/doc/python_api/rst/change_log.rst +++ b/doc/python_api/rst/change_log.rst @@ -6,386 +6,68 @@ Blender API Change Log .. note, this document is auto generated by sphinx_changelog_gen.py -2.79 to 2.80 +2.83 to 2.90 ============ -bpy.types.ActionGroup ---------------------- - -Added -^^^^^ - -* :class:`bpy.types.ActionGroup.show_expanded_graph` - -bpy.types.AnimData ------------------- - -Added -^^^^^ - -* :class:`bpy.types.AnimData.nla_tweak_strip_time_to_scene` - -bpy.types.AnimDataDrivers -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.AnimDataDrivers.new` -* :class:`bpy.types.AnimDataDrivers.remove` - -bpy.types.AnimViz ------------------ - -Removed -^^^^^^^ - -* **onion_skin_frames** - -bpy.types.AnimVizMotionPaths ----------------------------- - -Added -^^^^^ - -* :class:`bpy.types.AnimVizMotionPaths.has_motion_paths` - -bpy.types.Area --------------- - -Added -^^^^^ - -* :class:`bpy.types.Area.ui_type` - -bpy.types.BlendData -------------------- +bpy.types.CyclesPreferences +--------------------------- Added ^^^^^ -* :class:`bpy.types.BlendData.collections` -* :class:`bpy.types.BlendData.lightprobes` -* :class:`bpy.types.BlendData.lights` -* :class:`bpy.types.BlendData.workspaces` - -Removed -^^^^^^^ - -* **groups** -* **lamps** - -Renamed -^^^^^^^ - -* **grease_pencil** -> :class:`bpy.types.BlendData.grease_pencils` - -bpy.types.BlendDataActions --------------------------- +* :class:`bpy.types.CyclesPreferences.peer_memory` -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataArmatures ----------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataBrushes --------------------------- +bpy.types.BakeSettings +---------------------- Added ^^^^^ -* :class:`bpy.types.BlendDataBrushes.create_gpencil_data` - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataCacheFiles ------------------------------ - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataCameras --------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataCurves -------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataFonts ------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataGreasePencils --------------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataImages -------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.BlendDataImages.new` (name, width, height, alpha, float_buffer, stereo3d, is_data), *was (name, width, height, alpha, float_buffer, stereo3d)* - -bpy.types.BlendDataLattices ---------------------------- - -Removed -^^^^^^^ - -* **is_updated** +* :class:`bpy.types.BakeSettings.max_ray_distance` bpy.types.BlendDataLibraries ---------------------------- -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataLineStyles ------------------------------ - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataMasks ------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataMaterials ----------------------------- - Added ^^^^^ -* :class:`bpy.types.BlendDataMaterials.create_gpencil_data` -* :class:`bpy.types.BlendDataMaterials.remove_gpencil_data` - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataMeshes -------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.BlendDataMeshes.new_from_object` (object, preserve_all_data_layers, depsgraph), *was (scene, object, apply_modifiers, settings, calc_tessface, calc_undeformed)* - -bpy.types.BlendDataMetaBalls ----------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataMovieClips ------------------------------ - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataNodeTrees ----------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataObjects --------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataPaintCurves ------------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataPalettes ---------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataParticles ----------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataScenes -------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataScreens --------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataSounds -------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataSpeakers ---------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataTexts ------------------------- +* :class:`bpy.types.BlendDataLibraries.remove` -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataTextures ---------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataWindowManagers +bpy.types.BrushCapabilitiesSculpt --------------------------------- -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.BlendDataWorlds -------------------------- - -Removed -^^^^^^^ - -* **is_updated** - -bpy.types.Bone --------------- - Added ^^^^^ -* :class:`bpy.types.Bone.AxisRollFromMatrix` -* :class:`bpy.types.Bone.MatrixFromAxisRoll` -* :class:`bpy.types.Bone.bbone_custom_handle_end` -* :class:`bpy.types.Bone.bbone_custom_handle_start` -* :class:`bpy.types.Bone.bbone_easein` -* :class:`bpy.types.Bone.bbone_easeout` -* :class:`bpy.types.Bone.bbone_handle_type_end` -* :class:`bpy.types.Bone.bbone_handle_type_start` -* :class:`bpy.types.Bone.bbone_scaleinx` -* :class:`bpy.types.Bone.bbone_scaleiny` -* :class:`bpy.types.Bone.bbone_scaleoutx` -* :class:`bpy.types.Bone.bbone_scaleouty` -* :class:`bpy.types.Bone.convert_local_to_pose` - -Removed -^^^^^^^ +* :class:`bpy.types.BrushCapabilitiesSculpt.has_color` -* **bbone_in** -* **bbone_out** -* **bbone_scalein** -* **bbone_scaleout** - -bpy.types.ClothCollisionSettings --------------------------------- +bpy.types.BrushGpencilSettings +------------------------------ Added ^^^^^ -* :class:`bpy.types.ClothCollisionSettings.collection` -* :class:`bpy.types.ClothCollisionSettings.impulse_clamp` -* :class:`bpy.types.ClothCollisionSettings.self_impulse_clamp` - -Removed -^^^^^^^ - -* **distance_repel** -* **group** -* **repel_force** -* **self_collision_quality** +* :class:`bpy.types.BrushGpencilSettings.curve_random_hue` +* :class:`bpy.types.BrushGpencilSettings.curve_random_pressure` +* :class:`bpy.types.BrushGpencilSettings.curve_random_saturation` +* :class:`bpy.types.BrushGpencilSettings.curve_random_strength` +* :class:`bpy.types.BrushGpencilSettings.curve_random_uv` +* :class:`bpy.types.BrushGpencilSettings.curve_random_value` +* :class:`bpy.types.BrushGpencilSettings.random_hue_factor` +* :class:`bpy.types.BrushGpencilSettings.random_saturation_factor` +* :class:`bpy.types.BrushGpencilSettings.random_value_factor` +* :class:`bpy.types.BrushGpencilSettings.use_random_press_hue` +* :class:`bpy.types.BrushGpencilSettings.use_random_press_radius` +* :class:`bpy.types.BrushGpencilSettings.use_random_press_sat` +* :class:`bpy.types.BrushGpencilSettings.use_random_press_strength` +* :class:`bpy.types.BrushGpencilSettings.use_random_press_uv` +* :class:`bpy.types.BrushGpencilSettings.use_random_press_val` +* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_hue` +* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_radius` +* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_sat` +* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_strength` +* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_uv` +* :class:`bpy.types.BrushGpencilSettings.use_stroke_random_val` bpy.types.ClothSettings ----------------------- @@ -393,150 +75,7 @@ bpy.types.ClothSettings Added ^^^^^ -* :class:`bpy.types.ClothSettings.bending_model` -* :class:`bpy.types.ClothSettings.compression_damping` -* :class:`bpy.types.ClothSettings.compression_stiffness` -* :class:`bpy.types.ClothSettings.compression_stiffness_max` -* :class:`bpy.types.ClothSettings.shear_damping` -* :class:`bpy.types.ClothSettings.shear_stiffness` -* :class:`bpy.types.ClothSettings.shear_stiffness_max` -* :class:`bpy.types.ClothSettings.tension_damping` -* :class:`bpy.types.ClothSettings.tension_stiffness` -* :class:`bpy.types.ClothSettings.tension_stiffness_max` -* :class:`bpy.types.ClothSettings.vertex_group_shear_stiffness` - -Removed -^^^^^^^ - -* **spring_damping** -* **structural_stiffness** -* **structural_stiffness_max** -* **use_pin_cloth** -* **use_stiffness_scale** -* **vel_damping** - -bpy.types.CollisionSettings ---------------------------- - -Added -^^^^^ - -* :class:`bpy.types.CollisionSettings.cloth_friction` -* :class:`bpy.types.CollisionSettings.use_culling` -* :class:`bpy.types.CollisionSettings.use_normal` - -bpy.types.ColorManagedInputColorspaceSettings ---------------------------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ColorManagedInputColorspaceSettings.is_data` - -bpy.types.CopyScaleConstraint ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.CopyScaleConstraint.power` -* :class:`bpy.types.CopyScaleConstraint.use_add` - -bpy.types.FloorConstraint -------------------------- - -Removed -^^^^^^^ - -* **use_sticky** - -bpy.types.MaintainVolumeConstraint ----------------------------------- - -Added -^^^^^ - -* :class:`bpy.types.MaintainVolumeConstraint.mode` - -bpy.types.ShrinkwrapConstraint ------------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ShrinkwrapConstraint.cull_face` -* :class:`bpy.types.ShrinkwrapConstraint.track_axis` -* :class:`bpy.types.ShrinkwrapConstraint.use_invert_cull` -* :class:`bpy.types.ShrinkwrapConstraint.use_project_opposite` -* :class:`bpy.types.ShrinkwrapConstraint.use_track_normal` -* :class:`bpy.types.ShrinkwrapConstraint.wrap_mode` - -bpy.types.SplineIKConstraint ----------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SplineIKConstraint.use_original_scale` -* :class:`bpy.types.SplineIKConstraint.y_scale_mode` - -Removed -^^^^^^^ - -* **use_y_stretch** - -bpy.types.Context ------------------ - -Added -^^^^^ - -* :class:`bpy.types.Context.engine` -* :class:`bpy.types.Context.evaluated_depsgraph_get` - -Renamed -^^^^^^^ - -* **user_preferences** -> :class:`bpy.types.Context.collection` -* **user_preferences** -> :class:`bpy.types.Context.gizmo_group` -* **user_preferences** -> :class:`bpy.types.Context.layer_collection` -* **user_preferences** -> :class:`bpy.types.Context.preferences` -* **user_preferences** -> :class:`bpy.types.Context.view_layer` -* **user_preferences** -> :class:`bpy.types.Context.workspace` - -bpy.types.CurveMapping ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.CurveMapping.tone` - -bpy.types.Depsgraph -------------------- - -Added -^^^^^ - -* :class:`bpy.types.Depsgraph.debug_stats_gnuplot` -* :class:`bpy.types.Depsgraph.id_eval_get` -* :class:`bpy.types.Depsgraph.id_type_updated` -* :class:`bpy.types.Depsgraph.ids` -* :class:`bpy.types.Depsgraph.mode` -* :class:`bpy.types.Depsgraph.object_instances` -* :class:`bpy.types.Depsgraph.objects` -* :class:`bpy.types.Depsgraph.scene` -* :class:`bpy.types.Depsgraph.scene_eval` -* :class:`bpy.types.Depsgraph.updates` -* :class:`bpy.types.Depsgraph.view_layer` -* :class:`bpy.types.Depsgraph.view_layer_eval` - -Renamed -^^^^^^^ - -* **debug_graphviz** -> :class:`bpy.types.Depsgraph.debug_relations_graphviz` -* **debug_rebuild** -> :class:`bpy.types.Depsgraph.debug_tag_update` -* **debug_rebuild** -> :class:`bpy.types.Depsgraph.update` +* :class:`bpy.types.ClothSettings.fluid_density` bpy.types.DopeSheet ------------------- @@ -544,252 +83,49 @@ bpy.types.DopeSheet Added ^^^^^ -* :class:`bpy.types.DopeSheet.filter_collection` -* :class:`bpy.types.DopeSheet.show_cache_files` -* :class:`bpy.types.DopeSheet.show_lights` - -Removed -^^^^^^^ - -* **filter_group** -* **show_lamps** -* **show_only_group_objects** -* **show_only_matching_fcurves** -* **use_filter_text** - -bpy.types.Driver ----------------- - -Added -^^^^^ - -* :class:`bpy.types.Driver.is_simple_expression` - -Removed -^^^^^^^ - -* **show_debug_info** - -bpy.types.DynamicPaintBrushSettings ------------------------------------ - -Removed -^^^^^^^ - -* **material** -* **use_material** - -bpy.types.DynamicPaintSurface ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.DynamicPaintSurface.brush_collection` - -Removed -^^^^^^^ - -* **brush_group** -* **preview_id** -* **show_preview** -* **use_color_preview** - -bpy.types.EditBone ------------------- - -Added -^^^^^ - -* :class:`bpy.types.EditBone.bbone_custom_handle_end` -* :class:`bpy.types.EditBone.bbone_custom_handle_start` -* :class:`bpy.types.EditBone.bbone_easein` -* :class:`bpy.types.EditBone.bbone_easeout` -* :class:`bpy.types.EditBone.bbone_handle_type_end` -* :class:`bpy.types.EditBone.bbone_handle_type_start` -* :class:`bpy.types.EditBone.bbone_scaleinx` -* :class:`bpy.types.EditBone.bbone_scaleiny` -* :class:`bpy.types.EditBone.bbone_scaleoutx` -* :class:`bpy.types.EditBone.bbone_scaleouty` - -Removed -^^^^^^^ - -* **bbone_in** -* **bbone_out** -* **bbone_scalein** -* **bbone_scaleout** - -bpy.types.EffectorWeights -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.EffectorWeights.collection` - -Removed -^^^^^^^ - -* **group** - -bpy.types.Event ---------------- - -Added -^^^^^ - -* :class:`bpy.types.Event.is_mouse_absolute` - -bpy.types.FCurve ----------------- - -Added -^^^^^ - -* :class:`bpy.types.FCurve.auto_smoothing` -* :class:`bpy.types.FCurve.is_empty` - -bpy.types.FreestyleLineSet --------------------------- - -Added -^^^^^ - -* :class:`bpy.types.FreestyleLineSet.collection` -* :class:`bpy.types.FreestyleLineSet.collection_negation` -* :class:`bpy.types.FreestyleLineSet.select_by_collection` - -Removed -^^^^^^^ - -* **group** -* **group_negation** -* **select_by_group** - -bpy.types.GPUFXSettings ------------------------ - -Removed -^^^^^^^ - -* **dof** -* **use_dof** +* :class:`bpy.types.DopeSheet.show_hairs` +* :class:`bpy.types.DopeSheet.show_pointclouds` -bpy.types.GPencilFrames +bpy.types.FieldSettings ----------------------- -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.GPencilFrames.new` (frame_number, active), *was (frame_number)* - -bpy.types.GPencilLayer ----------------------- - Added ^^^^^ -* :class:`bpy.types.GPencilLayer.annotation_hide` -* :class:`bpy.types.GPencilLayer.blend_mode` -* :class:`bpy.types.GPencilLayer.channel_color` -* :class:`bpy.types.GPencilLayer.color` -* :class:`bpy.types.GPencilLayer.lock_material` -* :class:`bpy.types.GPencilLayer.mask_layer` -* :class:`bpy.types.GPencilLayer.pass_index` -* :class:`bpy.types.GPencilLayer.thickness` -* :class:`bpy.types.GPencilLayer.use_annotation_onion_skinning` -* :class:`bpy.types.GPencilLayer.use_solo_mode` -* :class:`bpy.types.GPencilLayer.viewlayer_render` - -Removed -^^^^^^^ - -* **unlock_color** -* **use_ghost_custom_colors** -* **use_ghosts_always** -* **use_volumetric_strokes** - -Renamed -^^^^^^^ - -* **after_color** -> :class:`bpy.types.GPencilLayer.annotation_onion_after_color` -* **before_color** -> :class:`bpy.types.GPencilLayer.annotation_onion_before_color` -* **ghost_after_range** -> :class:`bpy.types.GPencilLayer.annotation_onion_after_range` -* **ghost_before_range** -> :class:`bpy.types.GPencilLayer.annotation_onion_before_range` -* **show_x_ray** -> :class:`bpy.types.GPencilLayer.show_in_front` +* :class:`bpy.types.FieldSettings.wind_factor` -bpy.types.GPencilSculptBrush +bpy.types.FileSelectIDFilter ---------------------------- Added ^^^^^ -* :class:`bpy.types.GPencilSculptBrush.cursor_color_add` -* :class:`bpy.types.GPencilSculptBrush.cursor_color_sub` -* :class:`bpy.types.GPencilSculptBrush.use_cursor` -* :class:`bpy.types.GPencilSculptBrush.use_pressure_radius` -* :class:`bpy.types.GPencilSculptBrush.weight` - -Renamed -^^^^^^^ +* :class:`bpy.types.FileSelectIDFilter.filter_hair` +* :class:`bpy.types.FileSelectIDFilter.filter_pointcloud` +* :class:`bpy.types.FileSelectIDFilter.filter_simulation` -* **affect_pressure** -> :class:`bpy.types.GPencilSculptBrush.use_edit_pressure` - -bpy.types.GPencilSculptSettings -------------------------------- +bpy.types.FluidDomainSettings +----------------------------- Added ^^^^^ -* :class:`bpy.types.GPencilSculptSettings.guide` -* :class:`bpy.types.GPencilSculptSettings.intersection_threshold` -* :class:`bpy.types.GPencilSculptSettings.lock_axis` -* :class:`bpy.types.GPencilSculptSettings.multiframe_falloff_curve` -* :class:`bpy.types.GPencilSculptSettings.thickness_primitive_curve` -* :class:`bpy.types.GPencilSculptSettings.use_edit_uv` -* :class:`bpy.types.GPencilSculptSettings.use_multiframe_falloff` -* :class:`bpy.types.GPencilSculptSettings.use_thickness_curve` -* :class:`bpy.types.GPencilSculptSettings.weight_tool` - -Removed -^^^^^^^ - -* **lockaxis** -* **selection_alpha** +* :class:`bpy.types.FluidDomainSettings.cache_frame_offset` +* :class:`bpy.types.FluidDomainSettings.cache_resumable` +* :class:`bpy.types.FluidDomainSettings.sys_particle_maximum` Renamed ^^^^^^^ -* **affect_position** -> :class:`bpy.types.GPencilSculptSettings.use_edit_position` -* **affect_strength** -> :class:`bpy.types.GPencilSculptSettings.use_edit_strength` -* **affect_thickness** -> :class:`bpy.types.GPencilSculptSettings.use_edit_thickness` -* **tool** -> :class:`bpy.types.GPencilSculptSettings.sculpt_tool` +* **data_depth** -> :class:`bpy.types.FluidDomainSettings.openvdb_data_depth` -bpy.types.GPencilStroke ------------------------ +bpy.types.GPencilFrame +---------------------- Added ^^^^^ -* :class:`bpy.types.GPencilStroke.end_cap_mode` -* :class:`bpy.types.GPencilStroke.gradient_factor` -* :class:`bpy.types.GPencilStroke.gradient_shape` -* :class:`bpy.types.GPencilStroke.groups` -* :class:`bpy.types.GPencilStroke.is_nofill_stroke` -* :class:`bpy.types.GPencilStroke.material_index` -* :class:`bpy.types.GPencilStroke.start_cap_mode` - -Removed -^^^^^^^ - -* **color** -* **colorname** - -Renamed -^^^^^^^ - -* **draw_mode** -> :class:`bpy.types.GPencilStroke.display_mode` +* :class:`bpy.types.GPencilFrame.keyframe_type` bpy.types.GPencilStrokePoint ---------------------------- @@ -797,346 +133,52 @@ bpy.types.GPencilStrokePoint Added ^^^^^ -* :class:`bpy.types.GPencilStrokePoint.uv_factor` -* :class:`bpy.types.GPencilStrokePoint.uv_rotation` - -bpy.types.GPencilStrokes ------------------------- - -Added -^^^^^ - -* :class:`bpy.types.GPencilStrokes.close` - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.GPencilStrokes.new` (), *was (colorname)* - -bpy.types.GPencilTriangle -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.GPencilTriangle.uv1` -* :class:`bpy.types.GPencilTriangle.uv2` -* :class:`bpy.types.GPencilTriangle.uv3` - -bpy.types.GreasePencilLayers ----------------------------- - -Added -^^^^^ - -* :class:`bpy.types.GreasePencilLayers.active_note` -* :class:`bpy.types.GreasePencilLayers.move` - -bpy.types.Header ----------------- - -Added -^^^^^ - -* :class:`bpy.types.Header.bl_region_type` - -bpy.types.ID ------------- - -Added -^^^^^ - -* :class:`bpy.types.ID.evaluated_get` -* :class:`bpy.types.ID.is_evaluated` -* :class:`bpy.types.ID.name_full` -* :class:`bpy.types.ID.original` -* :class:`bpy.types.ID.override_create` -* :class:`bpy.types.ID.override_library` - -Removed -^^^^^^^ - -* **is_updated** -* **is_updated_data** - -bpy.types.Armature ------------------- - -Removed -^^^^^^^ - -* **deform_method** -* **ghost_frame_end** -* **ghost_frame_start** -* **ghost_size** -* **ghost_step** -* **ghost_type** -* **show_only_ghost_selected** -* **use_auto_ik** -* **use_deform_delay** - -Renamed -^^^^^^^ - -* **draw_type** -> :class:`bpy.types.Armature.display_type` - -bpy.types.Brush ---------------- - -Added -^^^^^ - -* :class:`bpy.types.Brush.falloff_angle` -* :class:`bpy.types.Brush.gpencil_settings` -* :class:`bpy.types.Brush.gpencil_tool` -* :class:`bpy.types.Brush.topology_rake_factor` -* :class:`bpy.types.Brush.use_frontface_falloff` -* :class:`bpy.types.Brush.use_paint_grease_pencil` -* :class:`bpy.types.Brush.use_paint_uv_sculpt` -* :class:`bpy.types.Brush.use_projected` -* :class:`bpy.types.Brush.uv_sculpt_tool` -* :class:`bpy.types.Brush.vertex_paint_capabilities` -* :class:`bpy.types.Brush.weight_paint_capabilities` -* :class:`bpy.types.Brush.weight_tool` - -bpy.types.CacheFile -------------------- - -Added -^^^^^ - -* :class:`bpy.types.CacheFile.frame_offset` - -bpy.types.Camera ----------------- - -Added -^^^^^ +* :class:`bpy.types.GPencilStrokePoint.uv_fill` -* :class:`bpy.types.Camera.background_images` -* :class:`bpy.types.Camera.display_size` -* :class:`bpy.types.Camera.show_background_images` -* :class:`bpy.types.Camera.show_composition_center` -* :class:`bpy.types.Camera.show_composition_center_diagonal` -* :class:`bpy.types.Camera.show_composition_golden` -* :class:`bpy.types.Camera.show_composition_golden_tria_a` -* :class:`bpy.types.Camera.show_composition_golden_tria_b` -* :class:`bpy.types.Camera.show_composition_harmony_tri_a` -* :class:`bpy.types.Camera.show_composition_harmony_tri_b` -* :class:`bpy.types.Camera.show_composition_thirds` - -Removed -^^^^^^^ - -* **dof_distance** -* **dof_object** -* **draw_size** -* **show_guide** - -Renamed -^^^^^^^ - -* **gpu_dof** -> :class:`bpy.types.Camera.dof` - -bpy.types.Curve +bpy.types.Gizmo --------------- Added ^^^^^ -* :class:`bpy.types.Curve.update_gpu_tag` - -Removed -^^^^^^^ +* :class:`bpy.types.Gizmo.hide_keymap` +* :class:`bpy.types.Gizmo.use_tooltip` -* **show_handles** -* **show_normal_face** - -bpy.types.TextCurve -------------------- - -Added -^^^^^ - -* :class:`bpy.types.TextCurve.overflow` - -bpy.types.GreasePencil ----------------------- +bpy.types.BuildGpencilModifier +------------------------------ Added ^^^^^ -* :class:`bpy.types.GreasePencil.after_color` -* :class:`bpy.types.GreasePencil.before_color` -* :class:`bpy.types.GreasePencil.edit_line_color` -* :class:`bpy.types.GreasePencil.ghost_after_range` -* :class:`bpy.types.GreasePencil.ghost_before_range` -* :class:`bpy.types.GreasePencil.grid` -* :class:`bpy.types.GreasePencil.is_annotation` -* :class:`bpy.types.GreasePencil.is_stroke_paint_mode` -* :class:`bpy.types.GreasePencil.is_stroke_sculpt_mode` -* :class:`bpy.types.GreasePencil.is_stroke_weight_mode` -* :class:`bpy.types.GreasePencil.onion_factor` -* :class:`bpy.types.GreasePencil.onion_keyframe_type` -* :class:`bpy.types.GreasePencil.onion_mode` -* :class:`bpy.types.GreasePencil.pixel_factor` -* :class:`bpy.types.GreasePencil.stroke_depth_order` -* :class:`bpy.types.GreasePencil.stroke_thickness_space` -* :class:`bpy.types.GreasePencil.use_adaptive_uv` -* :class:`bpy.types.GreasePencil.use_autolock_layers` -* :class:`bpy.types.GreasePencil.use_force_fill_recalc` -* :class:`bpy.types.GreasePencil.use_ghost_custom_colors` -* :class:`bpy.types.GreasePencil.use_ghosts_always` -* :class:`bpy.types.GreasePencil.use_multiedit` -* :class:`bpy.types.GreasePencil.use_onion_fade` -* :class:`bpy.types.GreasePencil.use_onion_loop` -* :class:`bpy.types.GreasePencil.zdepth_offset` +* :class:`bpy.types.BuildGpencilModifier.percentage_factor` +* :class:`bpy.types.BuildGpencilModifier.use_percentage` -Renamed -^^^^^^^ - -* **palettes** -> :class:`bpy.types.GreasePencil.materials` - -bpy.types.Image +bpy.types.Brush --------------- -Removed -^^^^^^^ - -* **field_order** -* **fps** -* **frame_end** -* **frame_start** -* **mapping** -* **tiles_x** -* **tiles_y** -* **use_alpha** -* **use_animation** -* **use_clamp_x** -* **use_clamp_y** -* **use_fields** -* **use_tiles** - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.Image.gl_load` (frame), *was (frame, filter, mag)* -* :class:`bpy.types.Image.gl_touch` (frame), *was (frame, filter, mag)* -* :class:`bpy.types.Image.pack` (data, data_len), *was (as_png, data, data_len)* - -bpy.types.Lattice ------------------ - -Added -^^^^^ - -* :class:`bpy.types.Lattice.update_gpu_tag` - -bpy.types.Library ------------------ - -Added -^^^^^ - -* :class:`bpy.types.Library.version` - -bpy.types.Material ------------------- - Added ^^^^^ -* :class:`bpy.types.Material.alpha_threshold` -* :class:`bpy.types.Material.blend_method` -* :class:`bpy.types.Material.grease_pencil` -* :class:`bpy.types.Material.is_grease_pencil` -* :class:`bpy.types.Material.metallic` -* :class:`bpy.types.Material.refraction_depth` -* :class:`bpy.types.Material.shadow_method` -* :class:`bpy.types.Material.show_transparent_back` -* :class:`bpy.types.Material.use_backface_culling` -* :class:`bpy.types.Material.use_preview_world` -* :class:`bpy.types.Material.use_screen_refraction` -* :class:`bpy.types.Material.use_sss_translucency` - -Removed -^^^^^^^ - -* **active_node_material** -* **active_texture** -* **active_texture_index** -* **alpha** -* **ambient** -* **darkness** -* **diffuse_fresnel** -* **diffuse_fresnel_factor** -* **diffuse_intensity** -* **diffuse_ramp** -* **diffuse_ramp_blend** -* **diffuse_ramp_factor** -* **diffuse_ramp_input** -* **diffuse_shader** -* **diffuse_toon_size** -* **diffuse_toon_smooth** -* **emit** -* **game_settings** -* **halo** -* **invert_z** -* **light_group** -* **mirror_color** -* **offset_z** -* **physics** -* **raytrace_mirror** -* **raytrace_transparency** -* **shadow_buffer_bias** -* **shadow_cast_alpha** -* **shadow_only_type** -* **shadow_ray_bias** -* **specular_alpha** -* **specular_hardness** -* **specular_ior** -* **specular_ramp** -* **specular_ramp_blend** -* **specular_ramp_factor** -* **specular_ramp_input** -* **specular_shader** -* **specular_slope** -* **specular_toon_size** -* **specular_toon_smooth** -* **strand** -* **subsurface_scattering** -* **texture_slots** -* **translucency** -* **transparency_method** -* **type** -* **use_cast_approximate** -* **use_cast_buffer_shadows** -* **use_cast_shadows** -* **use_cast_shadows_only** -* **use_cubic** -* **use_diffuse_ramp** -* **use_face_texture** -* **use_face_texture_alpha** -* **use_full_oversampling** -* **use_light_group_exclusive** -* **use_light_group_local** -* **use_mist** -* **use_object_color** -* **use_only_shadow** -* **use_ray_shadow_bias** -* **use_raytrace** -* **use_shadeless** -* **use_shadows** -* **use_sky** -* **use_specular_ramp** -* **use_tangent_shading** -* **use_textures** -* **use_transparency** -* **use_transparent_shadows** -* **use_uv_project** -* **use_vertex_color_light** -* **use_vertex_color_paint** -* **volume** +* :class:`bpy.types.Brush.density` +* :class:`bpy.types.Brush.disconnected_distance_max` +* :class:`bpy.types.Brush.flow` +* :class:`bpy.types.Brush.invert_density_pressure` +* :class:`bpy.types.Brush.invert_flow_pressure` +* :class:`bpy.types.Brush.invert_hardness_pressure` +* :class:`bpy.types.Brush.invert_wet_mix_pressure` +* :class:`bpy.types.Brush.invert_wet_persistence_pressure` +* :class:`bpy.types.Brush.pose_deform_type` +* :class:`bpy.types.Brush.slide_deform_type` +* :class:`bpy.types.Brush.smear_deform_type` +* :class:`bpy.types.Brush.tip_scale_x` +* :class:`bpy.types.Brush.use_connected_only` +* :class:`bpy.types.Brush.use_density_pressure` +* :class:`bpy.types.Brush.use_flow_pressure` +* :class:`bpy.types.Brush.use_hardness_pressure` +* :class:`bpy.types.Brush.use_wet_mix_pressure` +* :class:`bpy.types.Brush.use_wet_persistence_pressure` +* :class:`bpy.types.Brush.wet_mix` +* :class:`bpy.types.Brush.wet_persistence` bpy.types.Mesh -------------- @@ -1144,265 +186,16 @@ bpy.types.Mesh Added ^^^^^ -* :class:`bpy.types.Mesh.calc_loop_triangles` -* :class:`bpy.types.Mesh.count_selected_items` -* :class:`bpy.types.Mesh.face_maps` -* :class:`bpy.types.Mesh.loop_triangles` -* :class:`bpy.types.Mesh.update_gpu_tag` - -Removed -^^^^^^^ - -* **calc_tessface** -* **show_double_sided** -* **show_edge_bevel_weight** -* **show_edge_crease** -* **show_edge_seams** -* **show_edge_sharp** -* **show_edges** -* **show_extra_edge_angle** -* **show_extra_edge_length** -* **show_extra_face_angle** -* **show_extra_face_area** -* **show_extra_indices** -* **show_faces** -* **show_freestyle_edge_marks** -* **show_freestyle_face_marks** -* **show_normal_face** -* **show_normal_loop** -* **show_normal_vertex** -* **show_statvis** -* **show_weight** -* **tessface_uv_textures** -* **tessface_vertex_colors** -* **tessfaces** -* **uv_texture_clone** -* **uv_texture_clone_index** -* **uv_texture_stencil** -* **uv_texture_stencil_index** -* **uv_textures** - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.Mesh.update` (calc_edges, calc_edges_loose, calc_loop_triangles), *was (calc_edges, calc_tessface)* - -bpy.types.MetaBall ------------------- - -Added -^^^^^ - -* :class:`bpy.types.MetaBall.update_gpu_tag` - -bpy.types.MovieClip -------------------- - -Added -^^^^^ - -* :class:`bpy.types.MovieClip.fps` -* :class:`bpy.types.MovieClip.metadata` - -bpy.types.ShaderNodeTree ------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ShaderNodeTree.get_output_node` - -bpy.types.Object ----------------- - -Added -^^^^^ - -* :class:`bpy.types.Object.display` -* :class:`bpy.types.Object.display_type` -* :class:`bpy.types.Object.empty_image_depth` -* :class:`bpy.types.Object.empty_image_side` -* :class:`bpy.types.Object.face_maps` -* :class:`bpy.types.Object.grease_pencil_modifiers` -* :class:`bpy.types.Object.hide_get` -* :class:`bpy.types.Object.hide_set` -* :class:`bpy.types.Object.hide_viewport` -* :class:`bpy.types.Object.holdout_get` -* :class:`bpy.types.Object.indirect_only_get` -* :class:`bpy.types.Object.instance_collection` -* :class:`bpy.types.Object.instance_faces_scale` -* :class:`bpy.types.Object.instance_type` -* :class:`bpy.types.Object.is_from_instancer` -* :class:`bpy.types.Object.is_from_set` -* :class:`bpy.types.Object.local_view_get` -* :class:`bpy.types.Object.local_view_set` -* :class:`bpy.types.Object.proxy_collection` -* :class:`bpy.types.Object.select_get` -* :class:`bpy.types.Object.select_set` -* :class:`bpy.types.Object.shader_effects` -* :class:`bpy.types.Object.show_empty_image_orthographic` -* :class:`bpy.types.Object.show_empty_image_perspective` -* :class:`bpy.types.Object.show_in_front` -* :class:`bpy.types.Object.show_instancer_for_render` -* :class:`bpy.types.Object.show_instancer_for_viewport` -* :class:`bpy.types.Object.use_empty_image_alpha` -* :class:`bpy.types.Object.use_instance_faces_scale` -* :class:`bpy.types.Object.use_instance_vertices_rotation` -* :class:`bpy.types.Object.users_collection` -* :class:`bpy.types.Object.visible_get` - -Removed -^^^^^^^ - -* **draw_type** -* **dupli_faces_scale** -* **dupli_frames_end** -* **dupli_frames_off** -* **dupli_frames_on** -* **dupli_frames_start** -* **dupli_group** -* **dupli_list** -* **dupli_list_create** -* **dupli_type** -* **game** -* **grease_pencil** -* **hide** -* **is_visible** -* **layers** -* **layers_local_view** -* **lod_levels** -* **proxy_group** -* **select** -* **show_x_ray** -* **slow_parent_offset** -* **use_dupli_faces_scale** -* **use_dupli_frames_speed** -* **use_dupli_vertices_rotation** -* **use_extra_recalc_data** -* **use_extra_recalc_object** -* **use_slow_parent** -* **users_group** - -Renamed -^^^^^^^ - -* **draw_bounds_type** -> :class:`bpy.types.Object.display_bounds_type` -* **dupli_list_clear** -> :class:`bpy.types.Object.shape_key_clear` -* **dupli_list_clear** -> :class:`bpy.types.Object.to_mesh_clear` -* **empty_draw_size** -> :class:`bpy.types.Object.empty_display_size` -* **empty_draw_type** -> :class:`bpy.types.Object.empty_display_type` -* **is_duplicator** -> :class:`bpy.types.Object.is_instancer` - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.Object.calc_matrix_camera` (depsgraph, x, y, scale_x, scale_y), *was (x, y, scale_x, scale_y)* -* :class:`bpy.types.Object.camera_fit_coords` (depsgraph, coordinates), *was (scene, coordinates)* -* :class:`bpy.types.Object.closest_point_on_mesh` (origin, distance, depsgraph), *was (origin, distance)* -* :class:`bpy.types.Object.ray_cast` (origin, direction, distance, depsgraph), *was (origin, direction, distance)* -* :class:`bpy.types.Object.to_mesh` (preserve_all_data_layers, depsgraph), *was (scene, apply_modifiers, settings, calc_tessface, calc_undeformed)* - -bpy.types.ParticleSettings --------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ParticleSettings.collision_collection` -* :class:`bpy.types.ParticleSettings.display_size` -* :class:`bpy.types.ParticleSettings.instance_collection` -* :class:`bpy.types.ParticleSettings.instance_weights` -* :class:`bpy.types.ParticleSettings.radius_scale` -* :class:`bpy.types.ParticleSettings.root_radius` -* :class:`bpy.types.ParticleSettings.shape` -* :class:`bpy.types.ParticleSettings.tip_radius` -* :class:`bpy.types.ParticleSettings.twist` -* :class:`bpy.types.ParticleSettings.twist_curve` -* :class:`bpy.types.ParticleSettings.use_close_tip` -* :class:`bpy.types.ParticleSettings.use_twist_curve` -* :class:`bpy.types.ParticleSettings.use_whole_collection` - -Removed -^^^^^^^ - -* **billboard_align** -* **billboard_animation** -* **billboard_object** -* **billboard_offset** -* **billboard_offset_split** -* **billboard_size** -* **billboard_tilt** -* **billboard_tilt_random** -* **billboard_uv_split** -* **billboard_velocity_head** -* **billboard_velocity_tail** -* **collision_group** -* **cycles** -* **draw_size** -* **dupli_group** -* **dupli_weights** -* **lock_billboard** -* **simplify_rate** -* **simplify_refsize** -* **simplify_transition** -* **simplify_viewport** -* **use_render_emitter** -* **use_simplify** -* **use_simplify_viewport** -* **use_whole_group** - -Renamed -^^^^^^^ - -* **active_dupliweight** -> :class:`bpy.types.ParticleSettings.active_instanceweight` -* **active_dupliweight_index** -> :class:`bpy.types.ParticleSettings.active_instanceweight_index` -* **draw_color** -> :class:`bpy.types.ParticleSettings.display_color` -* **draw_method** -> :class:`bpy.types.ParticleSettings.display_method` -* **draw_percentage** -> :class:`bpy.types.ParticleSettings.display_percentage` -* **draw_step** -> :class:`bpy.types.ParticleSettings.display_step` -* **dupli_object** -> :class:`bpy.types.ParticleSettings.instance_object` -* **regrow_hair** -> :class:`bpy.types.ParticleSettings.use_regrow_hair` -* **use_global_dupli** -> :class:`bpy.types.ParticleSettings.use_global_instance` -* **use_group_count** -> :class:`bpy.types.ParticleSettings.use_collection_count` -* **use_group_pick_random** -> :class:`bpy.types.ParticleSettings.use_collection_pick_random` -* **use_rotation_dupli** -> :class:`bpy.types.ParticleSettings.use_rotation_instance` -* **use_scale_dupli** -> :class:`bpy.types.ParticleSettings.use_scale_instance` +* :class:`bpy.types.Mesh.sculpt_vertex_colors` +* :class:`bpy.types.Mesh.use_remesh_preserve_vertex_colors` bpy.types.Scene --------------- -Added -^^^^^ - -* :class:`bpy.types.Scene.collection` -* :class:`bpy.types.Scene.display` -* :class:`bpy.types.Scene.eevee` - -Removed -^^^^^^^ - -* **active_layer** -* **collada_export** -* **cursor_location** -* **depsgraph** -* **layers** -* **orientations** -* **update** -* **use_audio_sync** -* **use_frame_drop** - -Renamed -^^^^^^^ - -* **game_settings** -> :class:`bpy.types.Scene.cursor` -* **object_bases** -> :class:`bpy.types.Scene.transform_orientation_slots` -* **object_bases** -> :class:`bpy.types.Scene.view_layers` - Function Arguments ^^^^^^^^^^^^^^^^^^ -* :class:`bpy.types.Scene.ray_cast` (view_layer, origin, direction, distance), *was (origin, direction, distance)* -* :class:`bpy.types.Scene.statistics` (view_layer), *was ()* +* :class:`bpy.types.Scene.alembic_export` (filepath, frame_start, frame_end, xform_samples, geom_samples, shutter_open, shutter_close, selected_only, uvs, normals, vcolors, apply_subdiv, flatten, visible_objects_only, renderable_only, face_sets, subdiv_schema, export_hair, export_particles, packuv, scale, triangulate, quad_method, ngon_method), *was (filepath, frame_start, frame_end, xform_samples, geom_samples, shutter_open, shutter_close, selected_only, uvs, normals, vcolors, apply_subdiv, flatten, visible_objects_only, renderable_only, face_sets, subdiv_schema, export_hair, export_particles, compression_type, packuv, scale, triangulate, quad_method, ngon_method)* bpy.types.Screen ---------------- @@ -1410,190 +203,16 @@ bpy.types.Screen Added ^^^^^ -* :class:`bpy.types.Screen.is_temporary` -* :class:`bpy.types.Screen.show_statusbar` - -Removed -^^^^^^^ - -* **scene** - -bpy.types.Speaker ------------------ - -Removed -^^^^^^^ - -* **relative** - -bpy.types.Text --------------- - -Added -^^^^^ - -* :class:`bpy.types.Text.as_module` - -Removed -^^^^^^^ - -* **users_logic** - -bpy.types.ImageTexture ----------------------- - -Removed -^^^^^^^ - -* **use_derivative_map** - -Renamed -^^^^^^^ - -* **filter_probes** -> :class:`bpy.types.ImageTexture.filter_lightprobes` - -bpy.types.WindowManager ------------------------ - -Added -^^^^^ - -* :class:`bpy.types.WindowManager.gizmo_group_type_ensure` -* :class:`bpy.types.WindowManager.gizmo_group_type_unlink_delayed` -* :class:`bpy.types.WindowManager.operator_properties_last` -* :class:`bpy.types.WindowManager.popover` -* :class:`bpy.types.WindowManager.popover_begin__internal` -* :class:`bpy.types.WindowManager.popover_end__internal` -* :class:`bpy.types.WindowManager.preset_name` -* :class:`bpy.types.WindowManager.print_undo_steps` - -Renamed -^^^^^^^ - -* **pupmenu_begin__internal** -> :class:`bpy.types.WindowManager.popmenu_begin__internal` -* **pupmenu_end__internal** -> :class:`bpy.types.WindowManager.popmenu_end__internal` - -bpy.types.World ---------------- - -Added -^^^^^ - -* :class:`bpy.types.World.color` - -Removed -^^^^^^^ +* :class:`bpy.types.Screen.is_scrubbing` +* :class:`bpy.types.Screen.statusbar_info` -* **active_texture** -* **active_texture_index** -* **ambient_color** -* **color_range** -* **exposure** -* **horizon_color** -* **texture_slots** -* **use_sky_blend** -* **use_sky_paper** -* **use_sky_real** -* **zenith_color** - -bpy.types.ImageUser -------------------- +bpy.types.IDOverrideLibrary +--------------------------- Removed ^^^^^^^ -* **fields_per_frame** - -bpy.types.KeyConfig -------------------- - -Added -^^^^^ - -* :class:`bpy.types.KeyConfig.preferences` - -bpy.types.KeyConfigurations ---------------------------- - -Added -^^^^^ - -* :class:`bpy.types.KeyConfigurations.find_item_from_operator` -* :class:`bpy.types.KeyConfigurations.update` - -bpy.types.KeyMap ----------------- - -Added -^^^^^ - -* :class:`bpy.types.KeyMap.bl_owner_id` - -bpy.types.KeyMapItem --------------------- - -Added -^^^^^ - -* :class:`bpy.types.KeyMapItem.to_string` - -bpy.types.KeyMapItems ---------------------- - -Added -^^^^^ - -* :class:`bpy.types.KeyMapItems.find_from_operator` -* :class:`bpy.types.KeyMapItems.new_from_item` - -bpy.types.KeyMaps ------------------ - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.KeyMaps.new` (name, space_type, region_type, modal, tool), *was (name, space_type, region_type, modal)* - -bpy.types.LoopColors --------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.LoopColors.new` (name, do_init), *was (name)* - -bpy.types.Menu --------------- - -Added -^^^^^ - -* :class:`bpy.types.Menu.bl_owner_id` - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.Menu.draw_preset` (self, _context), *was (self, context)* -* :class:`bpy.types.Menu.path_menu` (self, searchpaths, operator, props_default, prop_filepath, filter_ext, filter_path, display_name, add_operator), *was (self, searchpaths, operator, props_default, prop_filepath, filter_ext, filter_path, display_name)* - -bpy.types.MeshUVLoopLayer -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.MeshUVLoopLayer.active` -* :class:`bpy.types.MeshUVLoopLayer.active_clone` -* :class:`bpy.types.MeshUVLoopLayer.active_render` - -bpy.types.ArrayModifier ------------------------ - -Added -^^^^^ - -* :class:`bpy.types.ArrayModifier.offset_u` -* :class:`bpy.types.ArrayModifier.offset_v` +* **auto_generate** bpy.types.BevelModifier ----------------------- @@ -1601,63 +220,14 @@ bpy.types.BevelModifier Added ^^^^^ -* :class:`bpy.types.BevelModifier.face_strength_mode` -* :class:`bpy.types.BevelModifier.harden_normals` -* :class:`bpy.types.BevelModifier.mark_seam` -* :class:`bpy.types.BevelModifier.mark_sharp` -* :class:`bpy.types.BevelModifier.miter_inner` -* :class:`bpy.types.BevelModifier.miter_outer` -* :class:`bpy.types.BevelModifier.spread` -* :class:`bpy.types.BevelModifier.width_pct` - -Removed -^^^^^^^ - -* **edge_weight_method** - -bpy.types.BooleanModifier -------------------------- +* :class:`bpy.types.BevelModifier.affect` +* :class:`bpy.types.BevelModifier.profile_type` Removed ^^^^^^^ -* **solver** - -bpy.types.HookModifier ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.HookModifier.vertex_indices` -* :class:`bpy.types.HookModifier.vertex_indices_set` - -bpy.types.MaskModifier ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.MaskModifier.threshold` - -bpy.types.MirrorModifier ------------------------- - -Added -^^^^^ - -* :class:`bpy.types.MirrorModifier.offset_u` -* :class:`bpy.types.MirrorModifier.offset_v` -* :class:`bpy.types.MirrorModifier.use_axis` -* :class:`bpy.types.MirrorModifier.use_bisect_axis` -* :class:`bpy.types.MirrorModifier.use_bisect_flip_axis` - -Removed -^^^^^^^ - -* **use_x** -* **use_y** -* **use_z** +* **use_custom_profile** +* **use_only_vertices** bpy.types.MultiresModifier -------------------------- @@ -1665,60 +235,17 @@ bpy.types.MultiresModifier Added ^^^^^ -* :class:`bpy.types.MultiresModifier.quality` -* :class:`bpy.types.MultiresModifier.use_creases` -* :class:`bpy.types.MultiresModifier.uv_smooth` - -Removed -^^^^^^^ - -* **use_subsurf_uv** - -bpy.types.NormalEditModifier ----------------------------- - -Added -^^^^^ - -* :class:`bpy.types.NormalEditModifier.no_polynors_fix` - -bpy.types.ParticleInstanceModifier ----------------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ParticleInstanceModifier.index_layer_name` -* :class:`bpy.types.ParticleInstanceModifier.particle_amount` -* :class:`bpy.types.ParticleInstanceModifier.particle_offset` -* :class:`bpy.types.ParticleInstanceModifier.particle_system` -* :class:`bpy.types.ParticleInstanceModifier.random_rotation` -* :class:`bpy.types.ParticleInstanceModifier.rotation` -* :class:`bpy.types.ParticleInstanceModifier.space` -* :class:`bpy.types.ParticleInstanceModifier.value_layer_name` - -bpy.types.ShrinkwrapModifier ----------------------------- +* :class:`bpy.types.MultiresModifier.use_custom_normals` -Added -^^^^^ - -* :class:`bpy.types.ShrinkwrapModifier.use_invert_cull` -* :class:`bpy.types.ShrinkwrapModifier.wrap_mode` - -Removed -^^^^^^^ - -* **use_keep_above_surface** - -bpy.types.SimpleDeformModifier ------------------------------- +bpy.types.OceanModifier +----------------------- Added ^^^^^ -* :class:`bpy.types.SimpleDeformModifier.deform_axis` -* :class:`bpy.types.SimpleDeformModifier.lock_z` +* :class:`bpy.types.OceanModifier.invert_spray` +* :class:`bpy.types.OceanModifier.spray_layer_name` +* :class:`bpy.types.OceanModifier.use_spray` bpy.types.SubsurfModifier ------------------------- @@ -1726,353 +253,186 @@ bpy.types.SubsurfModifier Added ^^^^^ -* :class:`bpy.types.SubsurfModifier.quality` -* :class:`bpy.types.SubsurfModifier.use_creases` -* :class:`bpy.types.SubsurfModifier.uv_smooth` +* :class:`bpy.types.SubsurfModifier.use_custom_normals` -Removed -^^^^^^^ - -* **use_opensubdiv** -* **use_subsurf_uv** - -bpy.types.TriangulateModifier ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.TriangulateModifier.keep_custom_normals` -* :class:`bpy.types.TriangulateModifier.min_vertices` - -bpy.types.UVProjectModifier ---------------------------- - -Removed -^^^^^^^ - -* **image** -* **use_image_override** - -bpy.types.Node --------------- - -Removed -^^^^^^^ - -* **shading_compatibility** - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.Node.poll` (_ntree), *was (ntree)* - -bpy.types.NodeCustomGroup -------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.NodeCustomGroup.poll` (_ntree), *was (ntree)* - -bpy.types.CompositorNodeMask ----------------------------- - -Removed -^^^^^^^ - -* **use_antialiasing** - -bpy.types.ShaderNodeAmbientOcclusion ------------------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ShaderNodeAmbientOcclusion.inside` -* :class:`bpy.types.ShaderNodeAmbientOcclusion.only_local` -* :class:`bpy.types.ShaderNodeAmbientOcclusion.samples` - -bpy.types.ShaderNodeBsdfPrincipled +bpy.types.VertexWeightEditModifier ---------------------------------- Added ^^^^^ -* :class:`bpy.types.ShaderNodeBsdfPrincipled.subsurface_method` +* :class:`bpy.types.VertexWeightEditModifier.normalize` -bpy.types.ShaderNodeOutputLineStyle ------------------------------------ +bpy.types.VertexWeightMixModifier +--------------------------------- Added ^^^^^ -* :class:`bpy.types.ShaderNodeOutputLineStyle.target` +* :class:`bpy.types.VertexWeightMixModifier.invert_vertex_group_a` +* :class:`bpy.types.VertexWeightMixModifier.invert_vertex_group_b` +* :class:`bpy.types.VertexWeightMixModifier.normalize` -bpy.types.ShaderNodeOutputMaterial ----------------------------------- +bpy.types.VertexWeightProximityModifier +--------------------------------------- Added ^^^^^ -* :class:`bpy.types.ShaderNodeOutputMaterial.target` +* :class:`bpy.types.VertexWeightProximityModifier.normalize` -bpy.types.ShaderNodeOutputWorld -------------------------------- +bpy.types.MovieTrackingCamera +----------------------------- Added ^^^^^ -* :class:`bpy.types.ShaderNodeOutputWorld.target` - -bpy.types.ShaderNodeTexCoord ----------------------------- - -Renamed -^^^^^^^ - -* **from_dupli** -> :class:`bpy.types.ShaderNodeTexCoord.from_instancer` - -bpy.types.ShaderNodeTexEnvironment ----------------------------------- - -Removed -^^^^^^^ - -* **color_space** - -bpy.types.ShaderNodeTexImage ----------------------------- - -Removed -^^^^^^^ - -* **color_space** - -bpy.types.ShaderNodeTexPointDensity ------------------------------------ - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.ShaderNodeTexPointDensity.cache_point_density` (depsgraph), *was (scene, settings)* -* :class:`bpy.types.ShaderNodeTexPointDensity.calc_point_density` (depsgraph), *was (scene, settings)* -* :class:`bpy.types.ShaderNodeTexPointDensity.calc_point_density_minmax` (depsgraph), *was (scene, settings)* +* :class:`bpy.types.MovieTrackingCamera.nuke_k1` +* :class:`bpy.types.MovieTrackingCamera.nuke_k2` -bpy.types.ShaderNodeTexVoronoi ------------------------------- +bpy.types.ShaderNodeTexSky +-------------------------- Added ^^^^^ -* :class:`bpy.types.ShaderNodeTexVoronoi.distance` -* :class:`bpy.types.ShaderNodeTexVoronoi.feature` - -bpy.types.ShaderNodeUVMap -------------------------- - -Renamed -^^^^^^^ - -* **from_dupli** -> :class:`bpy.types.ShaderNodeUVMap.from_instancer` +* :class:`bpy.types.ShaderNodeTexSky.air_density` +* :class:`bpy.types.ShaderNodeTexSky.altitude` +* :class:`bpy.types.ShaderNodeTexSky.dust_density` +* :class:`bpy.types.ShaderNodeTexSky.ozone_density` +* :class:`bpy.types.ShaderNodeTexSky.sun_disc` +* :class:`bpy.types.ShaderNodeTexSky.sun_elevation` +* :class:`bpy.types.ShaderNodeTexSky.sun_intensity` +* :class:`bpy.types.ShaderNodeTexSky.sun_rotation` +* :class:`bpy.types.ShaderNodeTexSky.sun_size` -bpy.types.NodeSocket --------------------- +bpy.types.NodeSocketInterface +----------------------------- Added ^^^^^ -* :class:`bpy.types.NodeSocket.draw_shape` +* :class:`bpy.types.NodeSocketInterface.NWViewerSocket` +* :class:`bpy.types.NodeSocketInterface.hide_value` -bpy.types.ObjectBase --------------------- +bpy.types.ObjectConstraints +--------------------------- Added ^^^^^ -* :class:`bpy.types.ObjectBase.hide_viewport` +* :class:`bpy.types.ObjectConstraints.copy` + +bpy.types.Sculpt +---------------- Removed ^^^^^^^ -* **layers** -* **layers_from_view** -* **layers_local_view** - -bpy.types.OperatorOptions -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.OperatorOptions.is_repeat` -* :class:`bpy.types.OperatorOptions.is_repeat_last` +* **use_threaded** -bpy.types.Paint +bpy.types.Panel --------------- Added ^^^^^ -* :class:`bpy.types.Paint.tool_slots` +* :class:`bpy.types.Panel.list_panel_index` -bpy.types.ImagePaint --------------------- +bpy.types.PoseBoneConstraints +----------------------------- Added ^^^^^ -* :class:`bpy.types.ImagePaint.interpolation` +* :class:`bpy.types.PoseBoneConstraints.copy` -bpy.types.Sculpt ----------------- +bpy.types.PreferencesEdit +------------------------- Added ^^^^^ -* :class:`bpy.types.Sculpt.show_mask` +* :class:`bpy.types.PreferencesEdit.collection_instance_empty_size` +* :class:`bpy.types.PreferencesEdit.use_duplicate_hair` +* :class:`bpy.types.PreferencesEdit.use_duplicate_pointcloud` -bpy.types.VertexPaint ---------------------- +bpy.types.PreferencesExperimental +--------------------------------- Added ^^^^^ -* :class:`bpy.types.VertexPaint.radial_symmetry` +* :class:`bpy.types.PreferencesExperimental.use_cycles_debug` +* :class:`bpy.types.PreferencesExperimental.use_new_hair_type` +* :class:`bpy.types.PreferencesExperimental.use_new_particle_system` +* :class:`bpy.types.PreferencesExperimental.use_sculpt_vertex_colors` Removed ^^^^^^^ -* **use_normal** -* **use_spray** +* **use_menu_search** -bpy.types.Panel ---------------- +bpy.types.PreferencesView +------------------------- Added ^^^^^ -* :class:`bpy.types.Panel.bl_order` -* :class:`bpy.types.Panel.bl_owner_id` -* :class:`bpy.types.Panel.bl_parent_id` -* :class:`bpy.types.Panel.bl_ui_units_x` -* :class:`bpy.types.Panel.draw_header_preset` -* :class:`bpy.types.Panel.is_popover` - -bpy.types.ParticleEdit ----------------------- +* :class:`bpy.types.PreferencesView.show_statusbar_memory` +* :class:`bpy.types.PreferencesView.show_statusbar_stats` +* :class:`bpy.types.PreferencesView.show_statusbar_version` +* :class:`bpy.types.PreferencesView.show_statusbar_vram` -Renamed -^^^^^^^ - -* **draw_step** -> :class:`bpy.types.ParticleEdit.display_step` - -bpy.types.ParticleSystem ------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ParticleSystem.invert_vertex_group_twist` -* :class:`bpy.types.ParticleSystem.vertex_group_twist` +bpy.types.CyclesCurveRenderSettings +----------------------------------- Removed ^^^^^^^ -* **billboard_normal_uv** -* **billboard_split_uv** -* **billboard_time_index_uv** -* **set_resolution** +* **cull_backfacing** +* **primitive** +* **resolution** +* **use_curves** -bpy.types.Pose --------------- +bpy.types.CyclesObjectSettings +------------------------------ Added ^^^^^ -* :class:`bpy.types.Pose.use_auto_ik` -* :class:`bpy.types.Pose.use_mirror_relative` -* :class:`bpy.types.Pose.use_mirror_x` +* :class:`bpy.types.CyclesObjectSettings.shadow_terminator_offset` -bpy.types.PoseBone ------------------- +bpy.types.CyclesRenderLayerSettings +----------------------------------- Added ^^^^^ -* :class:`bpy.types.PoseBone.bbone_easein` -* :class:`bpy.types.PoseBone.bbone_easeout` -* :class:`bpy.types.PoseBone.bbone_scaleinx` -* :class:`bpy.types.PoseBone.bbone_scaleiny` -* :class:`bpy.types.PoseBone.bbone_scaleoutx` -* :class:`bpy.types.PoseBone.bbone_scaleouty` -* :class:`bpy.types.PoseBone.bbone_segment_matrix` -* :class:`bpy.types.PoseBone.compute_bbone_handles` +* :class:`bpy.types.CyclesRenderLayerSettings.denoising_openimagedenoise_input_passes` Removed ^^^^^^^ -* **bbone_scalein** -* **bbone_scaleout** -* **use_bbone_custom_handles** -* **use_bbone_relative_end_handle** -* **use_bbone_relative_start_handle** - -bpy.types.Property ------------------- - -Added -^^^^^ - -* :class:`bpy.types.Property.is_overridable` -* :class:`bpy.types.Property.tags` - -bpy.types.BoolProperty ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.BoolProperty.array_dimensions` - -bpy.types.FloatProperty ------------------------ - -Added -^^^^^ - -* :class:`bpy.types.FloatProperty.array_dimensions` +* **use_optix_denoising** -bpy.types.IntProperty ---------------------- - -Added -^^^^^ - -* :class:`bpy.types.IntProperty.array_dimensions` - -bpy.types.Region ----------------- +bpy.types.CyclesRenderSettings +------------------------------ Added ^^^^^ -* :class:`bpy.types.Region.alignment` +* :class:`bpy.types.CyclesRenderSettings.debug_optix_curves_api` +* :class:`bpy.types.CyclesRenderSettings.denoiser` +* :class:`bpy.types.CyclesRenderSettings.preview_denoiser` +* :class:`bpy.types.CyclesRenderSettings.use_denoising` +* :class:`bpy.types.CyclesRenderSettings.use_preview_denoising` Removed ^^^^^^^ -* **id** - -bpy.types.RegionView3D ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.RegionView3D.clip_planes` -* :class:`bpy.types.RegionView3D.is_orthographic_side_view` -* :class:`bpy.types.RegionView3D.use_clip_planes` +* **preview_denoising** +* **use_bvh_embree** bpy.types.RenderEngine ---------------------- @@ -2080,64 +440,20 @@ bpy.types.RenderEngine Added ^^^^^ -* :class:`bpy.types.RenderEngine.bl_use_eevee_viewport` -* :class:`bpy.types.RenderEngine.free_blender_memory` -* :class:`bpy.types.RenderEngine.get_preview_pixel_size` -* :class:`bpy.types.RenderEngine.get_result` - -Removed -^^^^^^^ - -* **bl_use_exclude_layers** -* **bl_use_shading_nodes** -* **bl_use_texture_preview** +* :class:`bpy.types.RenderEngine.bl_use_gpu_context` Function Arguments ^^^^^^^^^^^^^^^^^^ -* :class:`bpy.types.RenderEngine.bake` (depsgraph, object, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result), *was (scene, object, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)* -* :class:`bpy.types.RenderEngine.camera_model_matrix` (camera, use_spherical_stereo), *was (camera, use_spherical_stereo, r_model_matrix)* -* :class:`bpy.types.RenderEngine.register_pass` (scene, view_layer, name, channels, chanid, type), *was (scene, srl, name, channels, chanid, type)* -* :class:`bpy.types.RenderEngine.render` (depsgraph), *was (scene)* -* :class:`bpy.types.RenderEngine.update` (data, depsgraph), *was (data, scene)* -* :class:`bpy.types.RenderEngine.view_draw` (context, depsgraph), *was (context)* -* :class:`bpy.types.RenderEngine.view_update` (context, depsgraph), *was (context)* - -bpy.types.RenderLayer ---------------------- - -Removed -^^^^^^^ +* :class:`bpy.types.RenderEngine.bake` (depsgraph, object, pass_type, pass_filter, width, height), *was (depsgraph, object, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)* -* **exclude_ambient_occlusion** -* **exclude_emit** -* **exclude_environment** -* **exclude_indirect** -* **exclude_reflection** -* **exclude_refraction** -* **exclude_shadow** -* **exclude_specular** -* **layers** -* **layers_exclude** -* **layers_zmask** -* **light_override** -* **material_override** -* **use** -* **use_freestyle** -* **use_pass_color** -* **use_pass_diffuse** -* **use_pass_indirect** -* **use_pass_reflection** -* **use_pass_refraction** -* **use_pass_specular** - -bpy.types.RenderResult ----------------------- +bpy.types.CYCLES +---------------- -Added -^^^^^ +Function Arguments +^^^^^^^^^^^^^^^^^^ -* :class:`bpy.types.RenderResult.stamp_data_add_field` +* :class:`bpy.types.CYCLES.bake` (self, depsgraph, obj, pass_type, pass_filter, width, height), *was (self, depsgraph, obj, pass_type, pass_filter, object_id, pixel_array, num_pixels, depth, result)* bpy.types.RenderSettings ------------------------ @@ -2145,132 +461,27 @@ bpy.types.RenderSettings Added ^^^^^ -* :class:`bpy.types.RenderSettings.film_transparent` -* :class:`bpy.types.RenderSettings.hair_subdiv` -* :class:`bpy.types.RenderSettings.hair_type` -* :class:`bpy.types.RenderSettings.preview_pixel_size` -* :class:`bpy.types.RenderSettings.simplify_gpencil` -* :class:`bpy.types.RenderSettings.simplify_gpencil_blend` -* :class:`bpy.types.RenderSettings.simplify_gpencil_onplay` -* :class:`bpy.types.RenderSettings.simplify_gpencil_remove_lines` -* :class:`bpy.types.RenderSettings.simplify_gpencil_shader_fx` -* :class:`bpy.types.RenderSettings.simplify_gpencil_view_fill` -* :class:`bpy.types.RenderSettings.simplify_gpencil_view_modifier` -* :class:`bpy.types.RenderSettings.use_sequencer_override_scene_strip` -* :class:`bpy.types.RenderSettings.use_simplify_smoke_highres` -* :class:`bpy.types.RenderSettings.use_stamp_frame_range` -* :class:`bpy.types.RenderSettings.use_stamp_hostname` +* :class:`bpy.types.RenderSettings.metadata_input` Removed ^^^^^^^ -* **alpha_mode** -* **antialiasing_samples** -* **bake_aa_mode** -* **bake_distance** -* **bake_normal_space** -* **bake_quad_split** -* **edge_color** -* **edge_threshold** -* **field_order** -* **layers** -* **motion_blur_samples** -* **octree_resolution** -* **pixel_filter_type** -* **raytrace_method** -* **simplify_ao_sss** -* **simplify_shadow_samples** -* **use_antialiasing** -* **use_bake_antialiasing** -* **use_bake_normalize** -* **use_bake_to_vertex_color** -* **use_edge_enhance** -* **use_envmaps** -* **use_fields** -* **use_fields_still** -* **use_free_image_textures** -* **use_game_engine** -* **use_instances** -* **use_local_coords** -* **use_raytrace** -* **use_sequencer_gl_textured_solid** -* **use_shading_nodes** -* **use_shadows** -* **use_simplify_triangulate** -* **use_sss** -* **use_textures** -* **use_world_space_shading** - -bpy.types.RenderSlot --------------------- - -Added -^^^^^ - -* :class:`bpy.types.RenderSlot.clear` +* **use_stamp_strip_meta** -bpy.types.RenderSlots ---------------------- - -Added -^^^^^ - -* :class:`bpy.types.RenderSlots.new` - -bpy.types.RigidBodyConstraint ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.RigidBodyConstraint.spring_type` - -bpy.types.RigidBodyObject -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.RigidBodyObject.collision_collections` - -Removed -^^^^^^^ - -* **collision_groups** - -bpy.types.RigidBodyWorld ------------------------- +bpy.types.SceneEEVEE +-------------------- Added ^^^^^ -* :class:`bpy.types.RigidBodyWorld.collection` +* :class:`bpy.types.SceneEEVEE.motion_blur_depth_scale` +* :class:`bpy.types.SceneEEVEE.motion_blur_max` +* :class:`bpy.types.SceneEEVEE.motion_blur_steps` Removed ^^^^^^^ -* **group** - -bpy.types.SPHFluidSettings --------------------------- - -Renamed -^^^^^^^ - -* **factor_radius** -> :class:`bpy.types.SPHFluidSettings.use_factor_radius` -* **factor_repulsion** -> :class:`bpy.types.SPHFluidSettings.use_factor_repulsion` -* **factor_rest_length** -> :class:`bpy.types.SPHFluidSettings.use_factor_rest_length` -* **factor_stiff_viscosity** -> :class:`bpy.types.SPHFluidSettings.use_factor_stiff_viscosity` - -bpy.types.SceneObjects ----------------------- - -Removed -^^^^^^^ - -* **active** -* **link** -* **unlink** +* **motion_blur_samples** bpy.types.Sequence ------------------ @@ -2278,92 +489,15 @@ bpy.types.Sequence Added ^^^^^ -* :class:`bpy.types.Sequence.override_cache_settings` -* :class:`bpy.types.Sequence.use_cache_composite` -* :class:`bpy.types.Sequence.use_cache_preprocessed` -* :class:`bpy.types.Sequence.use_cache_raw` - -bpy.types.EffectSequence ------------------------- - -Added -^^^^^ - -* :class:`bpy.types.EffectSequence.playback_direction` - -Removed -^^^^^^^ - -* **use_reverse_frames** +* :class:`bpy.types.Sequence.invalidate_cache` bpy.types.SpeedControlSequence ------------------------------ -Renamed -^^^^^^^ - -* **scale_to_length** -> :class:`bpy.types.SpeedControlSequence.use_scale_to_length` - -bpy.types.TextSequence ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.TextSequence.font` - -bpy.types.ImageSequence ------------------------ - -Added -^^^^^ - -* :class:`bpy.types.ImageSequence.playback_direction` - -Removed -^^^^^^^ - -* **use_reverse_frames** - -bpy.types.MaskSequence ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.MaskSequence.playback_direction` - -Removed -^^^^^^^ - -* **use_reverse_frames** - -bpy.types.MetaSequence ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.MetaSequence.playback_direction` - -Removed -^^^^^^^ - -* **use_reverse_frames** - -bpy.types.MovieClipSequence ---------------------------- - Added ^^^^^ -* :class:`bpy.types.MovieClipSequence.fps` -* :class:`bpy.types.MovieClipSequence.playback_direction` - -Removed -^^^^^^^ - -* **use_reverse_frames** +* :class:`bpy.types.SpeedControlSequence.frame_interpolation_mode` bpy.types.MovieSequence ----------------------- @@ -2371,290 +505,20 @@ bpy.types.MovieSequence Added ^^^^^ -* :class:`bpy.types.MovieSequence.fps` -* :class:`bpy.types.MovieSequence.metadata` -* :class:`bpy.types.MovieSequence.playback_direction` - -Removed -^^^^^^^ +* :class:`bpy.types.MovieSequence.reload_if_needed` -* **use_reverse_frames** - -bpy.types.SceneSequence +bpy.types.ShaderFxPixel ----------------------- Added ^^^^^ -* :class:`bpy.types.SceneSequence.fps` -* :class:`bpy.types.SceneSequence.playback_direction` -* :class:`bpy.types.SceneSequence.scene_input` +* :class:`bpy.types.ShaderFxPixel.use_antialiasing` Removed ^^^^^^^ -* **use_reverse_frames** -* **use_sequence** - -bpy.types.SequenceEditor ------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SequenceEditor.recycle_max_cost` -* :class:`bpy.types.SequenceEditor.show_cache` -* :class:`bpy.types.SequenceEditor.show_cache_composite` -* :class:`bpy.types.SequenceEditor.show_cache_final_out` -* :class:`bpy.types.SequenceEditor.show_cache_preprocessed` -* :class:`bpy.types.SequenceEditor.show_cache_raw` -* :class:`bpy.types.SequenceEditor.use_cache_composite` -* :class:`bpy.types.SequenceEditor.use_cache_final` -* :class:`bpy.types.SequenceEditor.use_cache_preprocessed` -* :class:`bpy.types.SequenceEditor.use_cache_raw` - -bpy.types.ShapeKeyBezierPoint ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.ShapeKeyBezierPoint.radius` -* :class:`bpy.types.ShapeKeyBezierPoint.tilt` - -bpy.types.ShapeKeyCurvePoint ----------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ShapeKeyCurvePoint.radius` - -bpy.types.SmokeDomainSettings ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.SmokeDomainSettings.clipping` -* :class:`bpy.types.SmokeDomainSettings.collision_collection` -* :class:`bpy.types.SmokeDomainSettings.display_interpolation` -* :class:`bpy.types.SmokeDomainSettings.effector_collection` -* :class:`bpy.types.SmokeDomainSettings.fluid_collection` -* :class:`bpy.types.SmokeDomainSettings.temperature_grid` - -Removed -^^^^^^^ - -* **collision_group** -* **effector_group** -* **fluid_group** - -Renamed -^^^^^^^ - -* **draw_velocity** -> :class:`bpy.types.SmokeDomainSettings.show_velocity` -* **vector_draw_type** -> :class:`bpy.types.SmokeDomainSettings.vector_display_type` - -bpy.types.SoftBodySettings --------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SoftBodySettings.collision_collection` - -Removed -^^^^^^^ - -* **collision_group** - -bpy.types.Space ---------------- - -Added -^^^^^ - -* :class:`bpy.types.Space.show_region_header` - -bpy.types.SpaceClipEditor -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SpaceClipEditor.mask_display_type` -* :class:`bpy.types.SpaceClipEditor.show_annotation` -* :class:`bpy.types.SpaceClipEditor.show_region_hud` -* :class:`bpy.types.SpaceClipEditor.show_region_toolbar` -* :class:`bpy.types.SpaceClipEditor.show_region_ui` - -Removed -^^^^^^^ - -* **mask_draw_type** -* **show_grease_pencil** - -bpy.types.SpaceDopeSheetEditor ------------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SpaceDopeSheetEditor.cache_cloth` -* :class:`bpy.types.SpaceDopeSheetEditor.cache_dynamicpaint` -* :class:`bpy.types.SpaceDopeSheetEditor.cache_particles` -* :class:`bpy.types.SpaceDopeSheetEditor.cache_rigidbody` -* :class:`bpy.types.SpaceDopeSheetEditor.cache_smoke` -* :class:`bpy.types.SpaceDopeSheetEditor.cache_softbody` -* :class:`bpy.types.SpaceDopeSheetEditor.show_cache` -* :class:`bpy.types.SpaceDopeSheetEditor.show_extremes` -* :class:`bpy.types.SpaceDopeSheetEditor.show_interpolation` -* :class:`bpy.types.SpaceDopeSheetEditor.show_marker_lines` -* :class:`bpy.types.SpaceDopeSheetEditor.show_region_ui` -* :class:`bpy.types.SpaceDopeSheetEditor.ui_mode` - -bpy.types.SpaceFileBrowser --------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SpaceFileBrowser.show_region_toolbar` -* :class:`bpy.types.SpaceFileBrowser.show_region_ui` - -bpy.types.SpaceGraphEditor --------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SpaceGraphEditor.show_marker_lines` -* :class:`bpy.types.SpaceGraphEditor.show_region_ui` - -bpy.types.SpaceImageEditor --------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SpaceImageEditor.mask_display_type` -* :class:`bpy.types.SpaceImageEditor.show_annotation` -* :class:`bpy.types.SpaceImageEditor.show_region_hud` -* :class:`bpy.types.SpaceImageEditor.show_region_tool_header` -* :class:`bpy.types.SpaceImageEditor.show_region_toolbar` -* :class:`bpy.types.SpaceImageEditor.show_region_ui` -* :class:`bpy.types.SpaceImageEditor.ui_mode` - -Removed -^^^^^^^ - -* **mask_draw_type** -* **show_grease_pencil** - -Renamed -^^^^^^^ - -* **draw_channels** -> :class:`bpy.types.SpaceImageEditor.display_channels` - -bpy.types.SpaceNLA ------------------- - -Added -^^^^^ - -* :class:`bpy.types.SpaceNLA.show_marker_lines` -* :class:`bpy.types.SpaceNLA.show_region_ui` - -bpy.types.SpaceNodeEditor -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SpaceNodeEditor.backdrop_offset` -* :class:`bpy.types.SpaceNodeEditor.show_annotation` -* :class:`bpy.types.SpaceNodeEditor.show_region_toolbar` -* :class:`bpy.types.SpaceNodeEditor.show_region_ui` - -Removed -^^^^^^^ - -* **backdrop_x** -* **backdrop_y** -* **show_grease_pencil** - -bpy.types.SpaceOutliner ------------------------ - -Added -^^^^^ - -* :class:`bpy.types.SpaceOutliner.filter_id_type` -* :class:`bpy.types.SpaceOutliner.filter_state` -* :class:`bpy.types.SpaceOutliner.show_restrict_column_enable` -* :class:`bpy.types.SpaceOutliner.show_restrict_column_hide` -* :class:`bpy.types.SpaceOutliner.show_restrict_column_holdout` -* :class:`bpy.types.SpaceOutliner.show_restrict_column_indirect_only` -* :class:`bpy.types.SpaceOutliner.show_restrict_column_render` -* :class:`bpy.types.SpaceOutliner.show_restrict_column_select` -* :class:`bpy.types.SpaceOutliner.show_restrict_column_viewport` -* :class:`bpy.types.SpaceOutliner.use_filter_children` -* :class:`bpy.types.SpaceOutliner.use_filter_collection` -* :class:`bpy.types.SpaceOutliner.use_filter_id_type` -* :class:`bpy.types.SpaceOutliner.use_filter_object` -* :class:`bpy.types.SpaceOutliner.use_filter_object_armature` -* :class:`bpy.types.SpaceOutliner.use_filter_object_camera` -* :class:`bpy.types.SpaceOutliner.use_filter_object_content` -* :class:`bpy.types.SpaceOutliner.use_filter_object_empty` -* :class:`bpy.types.SpaceOutliner.use_filter_object_light` -* :class:`bpy.types.SpaceOutliner.use_filter_object_mesh` -* :class:`bpy.types.SpaceOutliner.use_filter_object_others` - -Removed -^^^^^^^ - -* **show_restrict_columns** - -bpy.types.SpaceProperties -------------------------- - -Removed -^^^^^^^ - -* **align** -* **texture_context** -* **use_limited_texture_context** - -bpy.types.SpaceSequenceEditor ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.SpaceSequenceEditor.show_annotation` -* :class:`bpy.types.SpaceSequenceEditor.show_marker_lines` -* :class:`bpy.types.SpaceSequenceEditor.show_region_ui` - -Removed -^^^^^^^ - -* **show_grease_pencil** - -Renamed -^^^^^^^ - -* **draw_overexposed** -> :class:`bpy.types.SpaceSequenceEditor.show_overexposed` -* **waveform_draw_type** -> :class:`bpy.types.SpaceSequenceEditor.waveform_display_type` - -bpy.types.SpaceTextEditor -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.SpaceTextEditor.show_region_footer` -* :class:`bpy.types.SpaceTextEditor.show_region_ui` +* **color** bpy.types.SpaceView3D --------------------- @@ -2662,99 +526,10 @@ bpy.types.SpaceView3D Added ^^^^^ -* :class:`bpy.types.SpaceView3D.icon_from_show_object_viewport` -* :class:`bpy.types.SpaceView3D.overlay` -* :class:`bpy.types.SpaceView3D.shading` -* :class:`bpy.types.SpaceView3D.show_gizmo` -* :class:`bpy.types.SpaceView3D.show_gizmo_camera_dof_distance` -* :class:`bpy.types.SpaceView3D.show_gizmo_camera_lens` -* :class:`bpy.types.SpaceView3D.show_gizmo_context` -* :class:`bpy.types.SpaceView3D.show_gizmo_empty_force_field` -* :class:`bpy.types.SpaceView3D.show_gizmo_empty_image` -* :class:`bpy.types.SpaceView3D.show_gizmo_light_look_at` -* :class:`bpy.types.SpaceView3D.show_gizmo_light_size` -* :class:`bpy.types.SpaceView3D.show_gizmo_navigate` -* :class:`bpy.types.SpaceView3D.show_gizmo_object_rotate` -* :class:`bpy.types.SpaceView3D.show_gizmo_object_scale` -* :class:`bpy.types.SpaceView3D.show_gizmo_object_translate` -* :class:`bpy.types.SpaceView3D.show_gizmo_tool` -* :class:`bpy.types.SpaceView3D.show_object_select_armature` -* :class:`bpy.types.SpaceView3D.show_object_select_camera` -* :class:`bpy.types.SpaceView3D.show_object_select_curve` -* :class:`bpy.types.SpaceView3D.show_object_select_empty` -* :class:`bpy.types.SpaceView3D.show_object_select_font` -* :class:`bpy.types.SpaceView3D.show_object_select_grease_pencil` -* :class:`bpy.types.SpaceView3D.show_object_select_lattice` -* :class:`bpy.types.SpaceView3D.show_object_select_light` -* :class:`bpy.types.SpaceView3D.show_object_select_light_probe` -* :class:`bpy.types.SpaceView3D.show_object_select_mesh` -* :class:`bpy.types.SpaceView3D.show_object_select_meta` -* :class:`bpy.types.SpaceView3D.show_object_select_speaker` -* :class:`bpy.types.SpaceView3D.show_object_select_surf` -* :class:`bpy.types.SpaceView3D.show_object_viewport_armature` -* :class:`bpy.types.SpaceView3D.show_object_viewport_camera` -* :class:`bpy.types.SpaceView3D.show_object_viewport_curve` -* :class:`bpy.types.SpaceView3D.show_object_viewport_empty` -* :class:`bpy.types.SpaceView3D.show_object_viewport_font` -* :class:`bpy.types.SpaceView3D.show_object_viewport_grease_pencil` -* :class:`bpy.types.SpaceView3D.show_object_viewport_lattice` -* :class:`bpy.types.SpaceView3D.show_object_viewport_light` -* :class:`bpy.types.SpaceView3D.show_object_viewport_light_probe` -* :class:`bpy.types.SpaceView3D.show_object_viewport_mesh` -* :class:`bpy.types.SpaceView3D.show_object_viewport_meta` -* :class:`bpy.types.SpaceView3D.show_object_viewport_speaker` -* :class:`bpy.types.SpaceView3D.show_object_viewport_surf` -* :class:`bpy.types.SpaceView3D.show_region_hud` -* :class:`bpy.types.SpaceView3D.show_region_tool_header` -* :class:`bpy.types.SpaceView3D.show_region_toolbar` -* :class:`bpy.types.SpaceView3D.show_region_ui` -* :class:`bpy.types.SpaceView3D.use_local_camera` - -Removed -^^^^^^^ - -* **active_layer** -* **background_images** -* **current_orientation** -* **cursor_location** -* **grid_lines** -* **grid_scale** -* **grid_scale_unit** -* **grid_subdivisions** -* **layers** -* **layers_local_view** -* **layers_used** -* **lock_camera_and_layers** -* **matcap_icon** -* **pivot_point** -* **show_all_objects_origin** -* **show_axis_x** -* **show_axis_y** -* **show_axis_z** -* **show_backface_culling** -* **show_background_images** -* **show_floor** -* **show_grease_pencil** -* **show_manipulator** -* **show_occlude_wire** -* **show_only_render** -* **show_outline_selected** -* **show_relationship_lines** -* **show_textured_shadeless** -* **show_textured_solid** -* **show_world** -* **transform_manipulators** -* **transform_orientation** -* **use_matcap** -* **use_occlude_geometry** -* **use_pivot_point_align** -* **viewport_shade** - -Renamed -^^^^^^^ - -* **tracks_draw_size** -> :class:`bpy.types.SpaceView3D.tracks_display_size` -* **tracks_draw_type** -> :class:`bpy.types.SpaceView3D.tracks_display_type` +* :class:`bpy.types.SpaceView3D.show_object_select_hair` +* :class:`bpy.types.SpaceView3D.show_object_select_pointcloud` +* :class:`bpy.types.SpaceView3D.show_object_viewport_hair` +* :class:`bpy.types.SpaceView3D.show_object_viewport_pointcloud` bpy.types.SpaceUVEditor ----------------------- @@ -2762,298 +537,17 @@ bpy.types.SpaceUVEditor Added ^^^^^ -* :class:`bpy.types.SpaceUVEditor.edge_display_type` -* :class:`bpy.types.SpaceUVEditor.pixel_snap_mode` -* :class:`bpy.types.SpaceUVEditor.show_edges` -* :class:`bpy.types.SpaceUVEditor.show_pixel_coords` - -Removed -^^^^^^^ - -* **edge_draw_type** -* **other_uv_filter** -* **show_normalized_coords** -* **show_other_objects** -* **use_snap_to_pixels** - -Renamed -^^^^^^^ - -* **draw_stretch_type** -> :class:`bpy.types.SpaceUVEditor.display_stretch_type` - -bpy.types.Spline ----------------- - -Added -^^^^^ - -* :class:`bpy.types.Spline.calc_length` - -bpy.types.Struct ----------------- - -Added -^^^^^ - -* :class:`bpy.types.Struct.property_tags` - -bpy.types.TexPaintSlot ----------------------- - -Added -^^^^^ - -* :class:`bpy.types.TexPaintSlot.is_valid` - -Removed -^^^^^^^ - -* **index** - -bpy.types.TextCharacterFormat ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.TextCharacterFormat.kerning` - -bpy.types.TextureSlot ---------------------- - -Removed -^^^^^^^ - -* **invert** -* **use_rgb_to_intensity** -* **use_stencil** - -bpy.types.LineStyleTextureSlot ------------------------------- - -Removed -^^^^^^^ - -* **use_tips** - -bpy.types.ParticleSettingsTextureSlot -------------------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ParticleSettingsTextureSlot.twist_factor` -* :class:`bpy.types.ParticleSettingsTextureSlot.use_map_twist` - -bpy.types.Theme ---------------- - -Removed -^^^^^^^ - -* **logic_editor** -* **timeline** +* :class:`bpy.types.SpaceUVEditor.uv_opacity` -Renamed -^^^^^^^ - -* **user_preferences** -> :class:`bpy.types.Theme.preferences` -* **user_preferences** -> :class:`bpy.types.Theme.statusbar` -* **user_preferences** -> :class:`bpy.types.Theme.topbar` - -bpy.types.ThemeClipEditor -------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeClipEditor.time_scrub_background` - -Removed -^^^^^^^ - -* **gp_vertex_select** -* **gp_vertex_size** - -Renamed -^^^^^^^ - -* **gp_vertex** -> :class:`bpy.types.ThemeClipEditor.metadatabg` -* **gp_vertex** -> :class:`bpy.types.ThemeClipEditor.metadatatext` - -bpy.types.ThemeDopeSheet ------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeDopeSheet.interpolation_line` -* :class:`bpy.types.ThemeDopeSheet.keyframe_movehold` -* :class:`bpy.types.ThemeDopeSheet.keyframe_movehold_selected` -* :class:`bpy.types.ThemeDopeSheet.preview_range` -* :class:`bpy.types.ThemeDopeSheet.time_scrub_background` - -bpy.types.ThemeGraphEditor --------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeGraphEditor.preview_range` -* :class:`bpy.types.ThemeGraphEditor.time_scrub_background` - -bpy.types.ThemeImageEditor --------------------------- - -Removed -^^^^^^^ - -* **gp_vertex** -* **gp_vertex_select** -* **gp_vertex_size** - -bpy.types.ThemeNLAEditor ------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeNLAEditor.preview_range` -* :class:`bpy.types.ThemeNLAEditor.time_scrub_background` - -bpy.types.ThemeNodeEditor -------------------------- - -Removed -^^^^^^^ - -* **gp_vertex** -* **gp_vertex_select** -* **gp_vertex_size** - -bpy.types.ThemeOutliner ------------------------ - -Added -^^^^^ - -* :class:`bpy.types.ThemeOutliner.active_object` -* :class:`bpy.types.ThemeOutliner.edited_object` -* :class:`bpy.types.ThemeOutliner.row_alternate` -* :class:`bpy.types.ThemeOutliner.selected_object` - -bpy.types.ThemePanelColors --------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemePanelColors.sub_back` - -Removed -^^^^^^^ - -* **show_back** -* **show_header** - -bpy.types.ThemeSequenceEditor ------------------------------ - -Added -^^^^^ - -* :class:`bpy.types.ThemeSequenceEditor.time_scrub_background` - -Removed -^^^^^^^ - -* **gp_vertex** -* **gp_vertex_select** -* **gp_vertex_size** - -bpy.types.ThemeSpaceGeneric ---------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeSpaceGeneric.execution_buts` -* :class:`bpy.types.ThemeSpaceGeneric.navigation_bar` - -bpy.types.ThemeSpaceGradient ----------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeSpaceGradient.execution_buts` -* :class:`bpy.types.ThemeSpaceGradient.navigation_bar` - -bpy.types.ThemeUserInterface ----------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeUserInterface.editor_outline` -* :class:`bpy.types.ThemeUserInterface.gizmo_a` -* :class:`bpy.types.ThemeUserInterface.gizmo_b` -* :class:`bpy.types.ThemeUserInterface.gizmo_hi` -* :class:`bpy.types.ThemeUserInterface.gizmo_primary` -* :class:`bpy.types.ThemeUserInterface.gizmo_secondary` -* :class:`bpy.types.ThemeUserInterface.icon_border_intensity` -* :class:`bpy.types.ThemeUserInterface.icon_collection` -* :class:`bpy.types.ThemeUserInterface.icon_modifier` -* :class:`bpy.types.ThemeUserInterface.icon_object` -* :class:`bpy.types.ThemeUserInterface.icon_object_data` -* :class:`bpy.types.ThemeUserInterface.icon_saturation` -* :class:`bpy.types.ThemeUserInterface.icon_scene` -* :class:`bpy.types.ThemeUserInterface.icon_shading` -* :class:`bpy.types.ThemeUserInterface.wcol_tab` -* :class:`bpy.types.ThemeUserInterface.wcol_toolbar_item` - -Removed -^^^^^^^ - -* **icon_file** - -bpy.types.ThemeView3D ---------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeView3D.object_origin_size` +bpy.types.ThemeInfo +------------------- Removed ^^^^^^^ -* **object_grouped** -* **object_grouped_active** - -Renamed -^^^^^^^ - -* **lamp** -> :class:`bpy.types.ThemeView3D.light` - -bpy.types.ThemeWidgetColors ---------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeWidgetColors.roundness` - -bpy.types.ThemeWidgetStateColors --------------------------------- - -Added -^^^^^ - -* :class:`bpy.types.ThemeWidgetStateColors.inner_changed` -* :class:`bpy.types.ThemeWidgetStateColors.inner_changed_sel` -* :class:`bpy.types.ThemeWidgetStateColors.inner_overridden` -* :class:`bpy.types.ThemeWidgetStateColors.inner_overridden_sel` +* **info_report_error** +* **info_report_info** +* **info_report_warning** bpy.types.ToolSettings ---------------------- @@ -3061,60 +555,8 @@ bpy.types.ToolSettings Added ^^^^^ -* :class:`bpy.types.ToolSettings.annotation_stroke_placement_view3d` -* :class:`bpy.types.ToolSettings.annotation_thickness` -* :class:`bpy.types.ToolSettings.gpencil_paint` -* :class:`bpy.types.ToolSettings.gpencil_selectmode` -* :class:`bpy.types.ToolSettings.gpencil_stroke_snap_mode` -* :class:`bpy.types.ToolSettings.lock_object_mode` -* :class:`bpy.types.ToolSettings.normal_vector` -* :class:`bpy.types.ToolSettings.transform_pivot_point` -* :class:`bpy.types.ToolSettings.use_gpencil_thumbnail_list` -* :class:`bpy.types.ToolSettings.use_gpencil_weight_data_add` -* :class:`bpy.types.ToolSettings.use_keyframe_cycle_aware` -* :class:`bpy.types.ToolSettings.use_proportional_connected` -* :class:`bpy.types.ToolSettings.use_proportional_edit` -* :class:`bpy.types.ToolSettings.use_proportional_projected` -* :class:`bpy.types.ToolSettings.use_snap_rotate` -* :class:`bpy.types.ToolSettings.use_snap_scale` -* :class:`bpy.types.ToolSettings.use_snap_translate` -* :class:`bpy.types.ToolSettings.use_transform_pivot_point_align` - -Removed -^^^^^^^ - -* **edge_path_mode** -* **etch_adaptive_limit** -* **etch_convert_mode** -* **etch_length_limit** -* **etch_number** -* **etch_roll_mode** -* **etch_side** -* **etch_subdivision_number** -* **etch_template** -* **gpencil_brushes** -* **gpencil_stroke_placement_image_editor** -* **gpencil_stroke_placement_view2d** -* **grease_pencil_source** -* **normal_size** -* **proportional_edit** -* **use_bone_sketching** -* **use_etch_autoname** -* **use_etch_overdraw** -* **use_etch_quick** -* **use_gpencil_continuous_drawing** -* **use_uv_sculpt** -* **uv_sculpt_tool** - -Renamed -^^^^^^^ - -* **edge_path_live_unwrap** -> :class:`bpy.types.ToolSettings.use_edge_path_live_unwrap` -* **gpencil_stroke_placement_sequencer_preview** -> :class:`bpy.types.ToolSettings.annotation_stroke_placement_image_editor` -* **gpencil_stroke_placement_sequencer_preview** -> :class:`bpy.types.ToolSettings.annotation_stroke_placement_sequencer_preview` -* **gpencil_stroke_placement_sequencer_preview** -> :class:`bpy.types.ToolSettings.annotation_stroke_placement_view2d` -* **snap_element** -> :class:`bpy.types.ToolSettings.snap_elements` -* **use_gpencil_additive_drawing** -> :class:`bpy.types.ToolSettings.use_gpencil_draw_additive` +* :class:`bpy.types.ToolSettings.use_transform_correct_face_attributes` +* :class:`bpy.types.ToolSettings.use_transform_correct_keep_connected` bpy.types.UILayout ------------------ @@ -3122,229 +564,45 @@ bpy.types.UILayout Added ^^^^^ -* :class:`bpy.types.UILayout.activate_init` -* :class:`bpy.types.UILayout.active_default` -* :class:`bpy.types.UILayout.direction` -* :class:`bpy.types.UILayout.emboss` -* :class:`bpy.types.UILayout.grid_flow` -* :class:`bpy.types.UILayout.menu_contents` -* :class:`bpy.types.UILayout.operator_menu_hold` -* :class:`bpy.types.UILayout.popover` -* :class:`bpy.types.UILayout.popover_group` -* :class:`bpy.types.UILayout.prop_tabs_enum` -* :class:`bpy.types.UILayout.prop_with_menu` -* :class:`bpy.types.UILayout.prop_with_popover` -* :class:`bpy.types.UILayout.template_ID_tabs` -* :class:`bpy.types.UILayout.template_greasepencil_color` -* :class:`bpy.types.UILayout.template_greasepencil_modifier` -* :class:`bpy.types.UILayout.template_icon` -* :class:`bpy.types.UILayout.template_recent_files` -* :class:`bpy.types.UILayout.template_search` -* :class:`bpy.types.UILayout.template_search_preview` -* :class:`bpy.types.UILayout.template_shaderfx` -* :class:`bpy.types.UILayout.ui_units_x` -* :class:`bpy.types.UILayout.ui_units_y` -* :class:`bpy.types.UILayout.use_property_decorate` -* :class:`bpy.types.UILayout.use_property_split` +* :class:`bpy.types.UILayout.prop_decorator` +* :class:`bpy.types.UILayout.template_constraint_header` +* :class:`bpy.types.UILayout.template_constraints` +* :class:`bpy.types.UILayout.template_grease_pencil_modifiers` +* :class:`bpy.types.UILayout.template_modifiers` Removed ^^^^^^^ -* **introspect** - -Renamed -^^^^^^^ - -* **template_header_3D** -> :class:`bpy.types.UILayout.separator_spacer` -* **template_header_3D** -> :class:`bpy.types.UILayout.template_header_3D_mode` -* **template_header_3D** -> :class:`bpy.types.UILayout.template_input_status` +* **template_constraint** +* **template_greasepencil_modifier** +* **template_modifier** Function Arguments ^^^^^^^^^^^^^^^^^^ -* :class:`bpy.types.UILayout.operator` (operator, text, text_ctxt, translate, icon, emboss, depress, icon_value), *was (operator, text, text_ctxt, translate, icon, emboss, icon_value)* -* :class:`bpy.types.UILayout.prop` (data, property, text, text_ctxt, translate, icon, expand, slider, toggle, icon_only, event, full_event, emboss, index, icon_value, invert_checkbox), *was (data, property, text, text_ctxt, translate, icon, expand, slider, toggle, icon_only, event, full_event, emboss, index, icon_value)* -* :class:`bpy.types.UILayout.separator` (factor), *was ()* -* :class:`bpy.types.UILayout.split` (factor, align), *was (percentage, align)* -* :class:`bpy.types.UILayout.template_ID` (data, property, new, open, unlink, filter, live_icon), *was (data, property, new, open, unlink)* -* :class:`bpy.types.UILayout.template_ID_preview` (data, property, new, open, unlink, rows, cols, filter, hide_buttons), *was (data, property, new, open, unlink, rows, cols)* -* :class:`bpy.types.UILayout.template_curve_mapping` (data, property, type, levels, brush, use_negative_slope, show_tone), *was (data, property, type, levels, brush, use_negative_slope)* -* :class:`bpy.types.UILayout.template_icon_view` (data, property, show_labels, scale, scale_popup), *was (data, property, show_labels, scale)* -* :class:`bpy.types.UILayout.template_list` (listtype_name, list_id, dataptr, propname, active_dataptr, active_propname, item_dyntip_propname, rows, maxrows, type, columns, sort_reverse, sort_lock), *was (listtype_name, list_id, dataptr, propname, active_dataptr, active_propname, item_dyntip_propname, rows, maxrows, type, columns)* - -bpy.types.UIList ----------------- - -Added -^^^^^ - -* :class:`bpy.types.UIList.use_filter_sort_lock` - -bpy.types.CLIP_UL_tracking_objects ----------------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ +* :class:`bpy.types.UILayout.column` (align, heading, heading_ctxt, translate), *was (align)* +* :class:`bpy.types.UILayout.row` (align, heading, heading_ctxt, translate), *was (align)* +* :class:`bpy.types.UILayout.template_shaderfx` (), *was (data)* -* :class:`bpy.types.CLIP_UL_tracking_objects.draw_item` (self, _context, layout, _data, item, _icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.FILEBROWSER_UL_dir ----------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.FILEBROWSER_UL_dir.draw_item` (self, _context, layout, _data, item, icon, _active_data, active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.GPENCIL_UL_layer --------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.GPENCIL_UL_layer.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.MASK_UL_layers ------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.MASK_UL_layers.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.MATERIAL_UL_matslots ------------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.MATERIAL_UL_matslots.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.MESH_UL_shape_keys ----------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.MESH_UL_shape_keys.draw_item` (self, _context, layout, _data, item, icon, active_data, _active_propname, index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.MESH_UL_vgroups -------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.MESH_UL_vgroups.draw_item` (self, _context, layout, _data, item, icon, _active_data_, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.NODE_UL_interface_sockets ------------------------------------ - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.NODE_UL_interface_sockets.draw_item` (self, context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.PARTICLE_UL_particle_systems --------------------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.PARTICLE_UL_particle_systems.draw_item` (self, _context, layout, data, item, icon, _active_data, _active_propname, _index, _flt_flag), *was (self, context, layout, data, item, icon, active_data, active_propname, index, flt_flag)* - -bpy.types.PHYSICS_UL_dynapaint_surfaces ---------------------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.PHYSICS_UL_dynapaint_surfaces.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.SCENE_UL_keying_set_paths ------------------------------------ - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.SCENE_UL_keying_set_paths.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.TEXTURE_UL_texpaintslots ----------------------------------- - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.TEXTURE_UL_texpaintslots.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.TEXTURE_UL_texslots ------------------------------ - -Function Arguments -^^^^^^^^^^^^^^^^^^ - -* :class:`bpy.types.TEXTURE_UL_texslots.draw_item` (self, _context, layout, _data, item, icon, _active_data, _active_propname, _index), *was (self, context, layout, data, item, icon, active_data, active_propname, index)* - -bpy.types.UVLoopLayers ----------------------- +bpy.types.View3DOverlay +----------------------- Added ^^^^^ -* :class:`bpy.types.UVLoopLayers.new` -* :class:`bpy.types.UVLoopLayers.remove` - -bpy.types.UnitSettings ----------------------- +* :class:`bpy.types.View3DOverlay.display_handle` +* :class:`bpy.types.View3DOverlay.show_stats` +* :class:`bpy.types.View3DOverlay.use_gpencil_canvas_xray` -Added -^^^^^ +Removed +^^^^^^^ -* :class:`bpy.types.UnitSettings.length_unit` -* :class:`bpy.types.UnitSettings.mass_unit` -* :class:`bpy.types.UnitSettings.time_unit` +* **show_curve_handles** -bpy.types.UserSolidLight +bpy.types.XrSessionState ------------------------ Added ^^^^^ -* :class:`bpy.types.UserSolidLight.smooth` - -bpy.types.Window ----------------- - -Added -^^^^^ - -* :class:`bpy.types.Window.event_simulate` -* :class:`bpy.types.Window.parent` -* :class:`bpy.types.Window.scene` -* :class:`bpy.types.Window.view_layer` -* :class:`bpy.types.Window.workspace` - -bpy.types.WorldLighting ------------------------ - -Removed -^^^^^^^ - -* **adapt_to_speed** -* **ao_blend_type** -* **bias** -* **correction** -* **environment_color** -* **environment_energy** -* **error_threshold** -* **falloff_strength** -* **gather_method** -* **indirect_bounces** -* **indirect_factor** -* **passes** -* **sample_method** -* **samples** -* **threshold** -* **use_cache** -* **use_environment_light** -* **use_falloff** -* **use_indirect_light** +* :class:`bpy.types.XrSessionState.reset_to_base_pose` diff --git a/extern/bullet2/CMakeLists.txt b/extern/bullet2/CMakeLists.txt index b386aa4035b..9d0557ded7d 100644 --- a/extern/bullet2/CMakeLists.txt +++ b/extern/bullet2/CMakeLists.txt @@ -419,7 +419,8 @@ if(MSVC) # bullet is responsible for quite a few silly warnings # suppress all of them. Not great, but they really needed # to sort that out themselves. - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W0") + remove_cc_flag("/W3") + add_c_flag("/W0") endif() blender_add_lib(extern_bullet "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp b/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp index d6d87d3a791..ba8cea8079f 100755 --- a/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp +++ b/extern/discregrid/discregrid/include/Discregrid/cubic_lagrange_discrete_grid.hpp @@ -9,7 +9,7 @@ class CubicLagrangeDiscreteGrid : public DiscreteGrid {
public:
- CubicLagrangeDiscreteGrid(){}
+ CubicLagrangeDiscreteGrid(){ this->m_n_cells = 0; }
CubicLagrangeDiscreteGrid(std::string const& filename);
CubicLagrangeDiscreteGrid(Eigen::AlignedBox3d const& domain,
std::array<unsigned int, 3> const& resolution);
diff --git a/extern/mantaflow/CMakeLists.txt b/extern/mantaflow/CMakeLists.txt index 989f45c69b4..8bb0bc69f98 100644 --- a/extern/mantaflow/CMakeLists.txt +++ b/extern/mantaflow/CMakeLists.txt @@ -64,9 +64,6 @@ endif() if(WITH_OPENVDB) add_definitions(-DOPENVDB=1) - if(NOT WIN32) - add_definitions(-DOPENVDB_STATICLIB) - endif() endif() if(WITH_OPENVDB_BLOSC) diff --git a/extern/quadriflow/CMakeLists.txt b/extern/quadriflow/CMakeLists.txt index bf64e5edb67..e24151d8bf6 100644 --- a/extern/quadriflow/CMakeLists.txt +++ b/extern/quadriflow/CMakeLists.txt @@ -28,9 +28,6 @@ if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_C_COMPILER_ID MATCHES "Clang") ) endif() -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) - if(WIN32) add_definitions(-D_USE_MATH_DEFINES) endif() diff --git a/extern/quadriflow/patches/blender.patch b/extern/quadriflow/patches/blender.patch index eceda489393..6405a47e262 100644 --- a/extern/quadriflow/patches/blender.patch +++ b/extern/quadriflow/patches/blender.patch @@ -1,3 +1,15 @@ +diff --git a/extern/quadriflow/src/loader.cpp b/extern/quadriflow/src/loader.cpp +--- a/extern/quadriflow/src/loader.cpp ++++ b/extern/quadriflow/src/loader.cpp +@@ -69,7 +69,7 @@ + }; + + /// Hash function for obj_vertex +- struct obj_vertexHash : std::unary_function<obj_vertex, size_t> { ++ struct obj_vertexHash { + std::size_t operator()(const obj_vertex &v) const { + size_t hash = std::hash<uint32_t>()(v.p); + hash = hash * 37 + std::hash<uint32_t>()(v.uv); diff --git a/extern/quadriflow/src/config.hpp b/extern/quadriflow/src/config.hpp index 842b885..bf597ad 100644 --- a/extern/quadriflow/src/config.hpp diff --git a/extern/quadriflow/src/loader.cpp b/extern/quadriflow/src/loader.cpp index aa27066e6e4..a1596eeff9a 100644 --- a/extern/quadriflow/src/loader.cpp +++ b/extern/quadriflow/src/loader.cpp @@ -69,7 +69,7 @@ void load(const char* filename, MatrixXd& V, MatrixXi& F) }; /// Hash function for obj_vertex - struct obj_vertexHash : std::unary_function<obj_vertex, size_t> { + struct obj_vertexHash { std::size_t operator()(const obj_vertex &v) const { size_t hash = std::hash<uint32_t>()(v.p); hash = hash * 37 + std::hash<uint32_t>()(v.uv); diff --git a/extern/softbody/src/admmpd_bvh.cpp b/extern/softbody/src/admmpd_bvh.cpp index b9e49a29668..e56729c579f 100644 --- a/extern/softbody/src/admmpd_bvh.cpp +++ b/extern/softbody/src/admmpd_bvh.cpp @@ -267,9 +267,14 @@ void Octree<T,DIM>::init(const MatrixXT *V, const Eigen::MatrixXi *F, int stopde boxes[i].extend(V->row(f[0]).transpose()); boxes[i].extend(V->row(f[1]).transpose()); boxes[i].extend(V->row(f[2]).transpose()); + boxes[i].extend(boxes[i].min()-VecType::Ones()*1e-4); + boxes[i].extend(boxes[i].max()+VecType::Ones()*1e-4); global_box.extend(boxes[i]); } + global_box.extend(global_box.min()-VecType::Ones()*1e-2); + global_box.extend(global_box.max()+VecType::Ones()*1e-2); + T halfwidth = global_box.sizes().maxCoeff()*0.5; m_root.reset( create_children(global_box.center(),halfwidth,stopdepth,V,F,queue,boxes) diff --git a/extern/softbody/src/admmpd_bvh.h b/extern/softbody/src/admmpd_bvh.h index 647442bc4d7..2ad53d0fad8 100644 --- a/extern/softbody/src/admmpd_bvh.h +++ b/extern/softbody/src/admmpd_bvh.h @@ -61,6 +61,12 @@ public: } }; + // Return AABB of root + Eigen::AlignedBox<T,DIM> bounds() const { + if (m_root) { return m_root->aabb; } + return Eigen::AlignedBox<T,DIM>(); + } + // Return ptr to the root node // Becomes invalidated after init() std::shared_ptr<Node> root() { return m_root; } diff --git a/extern/softbody/src/admmpd_collision.cpp b/extern/softbody/src/admmpd_collision.cpp index 78889b810b8..c6751b3c279 100644 --- a/extern/softbody/src/admmpd_collision.cpp +++ b/extern/softbody/src/admmpd_collision.cpp @@ -26,83 +26,110 @@ VFCollisionPair::VFCollisionPair() : q_bary(0,0,0) {} -void Collision::ObstacleData::clear() { - V = MatrixXd(); - F = MatrixXi(); - sdf = Discregrid::CubicLagrangeDiscreteGrid(); -} - - -bool Collision::set_obstacles( - const float *v0, - const float *v1, - int nv, - const unsigned int *faces, - int nf, - std::string *err) +bool Collision::ObstacleData::compute_sdf(int idx) { - (void)(v0); - if (nv==0 || nf==0) - { - if (err) { *err = "Collision obstacle has no verts or faces"; } - obsdata.clear(); + if (idx < 0 || idx >x1.size()) { return false; } - std::vector<double> v1_dbl(nv*3); - Eigen::AlignedBox<double,3> domain; - - if (obsdata.V.rows() != nv) - obsdata.V.resize(nv,3); - for (int i=0; i<nv; ++i) - { - for (int j=0; j<3; ++j) - { - obsdata.V(i,j) = v1[i*3+j]; - v1_dbl[i*3+j] = v1[i*3+j]; - } - domain.extend(obsdata.V.row(i).transpose()); - } - - if (obsdata.F.rows() != nf) - obsdata.F.resize(nf,3); - for (int i=0; i<nf; ++i) - { - for (int j=0; j<3; ++j) - obsdata.F(i,j) = faces[i*3+j]; + // There was an error in init + if (box[idx].isEmpty()) { + return false; } - // Is the mesh closed? - Discregrid::TriangleMesh tm(v1_dbl.data(), faces, nv, nf); + // Test that the mesh is closed + Discregrid::TriangleMesh tm( + (double const*)x1[idx].data(), + (unsigned int const*)F[idx].data(), + x1[idx].rows(), F[idx].rows()); if (!tm.is_closed()) { - if (err) { *err = "Collision obstacle not a closed mesh - ignoring"; } - obsdata.clear(); return false; } // Generate signed distance field - { - Discregrid::MeshDistance md(tm); - domain.max() += 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones(); - domain.min() -= 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones(); - std::array<unsigned int, 3> resolution; - resolution[0] = 30; resolution[1] = 30; resolution[2] = 30; - obsdata.sdf = Discregrid::CubicLagrangeDiscreteGrid(domain, resolution); - auto func = Discregrid::DiscreteGrid::ContinuousFunction{}; - std::vector<std::thread::id> thread_map; - md.set_thread_map(&thread_map); - func = [&md](Eigen::Vector3d const& xi) { - return md.signedDistanceCached(xi); - }; - obsdata.sdf.addFunction(func, &thread_map, false); + Discregrid::MeshDistance md(tm); + std::array<unsigned int, 3> resolution; + resolution[0] = 30; resolution[1] = 30; resolution[2] = 30; + sdf[idx] = Discregrid::CubicLagrangeDiscreteGrid(box[idx], resolution); + auto func = Discregrid::DiscreteGrid::ContinuousFunction{}; + std::vector<std::thread::id> thread_map; + md.set_thread_map(&thread_map); + func = [&md](Eigen::Vector3d const& xi) { + return md.signedDistanceCached(xi); + }; + sdf[idx].addFunction(func, &thread_map, false); + + if (sdf[idx].nCells()==0) { + return false; } + return true; +} - if (obsdata.sdf.nCells()==0) { - if (err) { *err = "SDF gen failed for collision obstacle"; } - obsdata.clear(); +bool Collision::set_obstacles( + std::vector<Eigen::MatrixXd> &v0, + std::vector<Eigen::MatrixXd> &v1, + std::vector<Eigen::MatrixXi> &F, + std::string *err) +{ + if (v0.size() != v1.size() || v0.size() != F.size()) { + if (err) { *err = "Bad dimensions on obstacle input"; } return false; } + // Copy the obstacle data from the input to the stored + // data container. If the vertex locations have changed, + // we need to recompute the SDF. Otherwise, leave it as is. + int n_obs_new = v0.size(); + int n_obs_old = obsdata.x0.size(); + obsdata.sdf.resize(n_obs_new); + obsdata.x0.resize(n_obs_new); + obsdata.x1.resize(n_obs_new); + obsdata.F.resize(n_obs_new); + obsdata.box.resize(n_obs_new); + + // We can use isApprox for testing if the obstacle has + // moved from the last call to set_obstacles. The SDF + // has limited accuracy anyway... + double approx_eps = 1e-6; + for (int i=0; i<n_obs_new; ++i) { + + bool reset_obs = false; + if (i >= n_obs_old) { + reset_obs=true; // is new obs + } + else if (!obsdata.x1[i].isApprox(v1[i],approx_eps) || + !obsdata.x0[i].isApprox(v0[i],approx_eps)) { + reset_obs = true; // is different than before + } + + if (reset_obs) { + + obsdata.box[i].setEmpty(); + int nv = v1[i].rows(); + for (int j=0; j<nv; ++j) { + obsdata.box[i].extend(v1[i].row(j).transpose()); + } + obsdata.box[i].max() += 1e-3 * obsdata.box[i].diagonal().norm() * Eigen::Vector3d::Ones(); + obsdata.box[i].min() -= 1e-3 * obsdata.box[i].diagonal().norm() * Eigen::Vector3d::Ones(); + + obsdata.sdf[i] = SDFType(); // clear old sdf + obsdata.x0[i] = v0[i]; + obsdata.x1[i] = v1[i]; + obsdata.F[i] = F[i].cast<unsigned int>(); + + // Determine if the triangle mesh is closed or not. + // We want to provide a warning if it is. + Discregrid::TriangleMesh tm( + (double const*)obsdata.x1[i].data(), + (unsigned int const*)obsdata.F[i].data(), + obsdata.x1[i].rows(), obsdata.F[i].rows()); + if (!tm.is_closed()) { + obsdata.box[i].setEmpty(); + if (err) { *err = "Collision obstacle not a closed mesh - ignoring"; } + } + } + } + return true; } // end add obstacle @@ -121,29 +148,31 @@ Collision::detect_against_obs( (void)(data); (void)(pt_t0); - std::pair<bool,VFCollisionPair> ret = - std::make_pair(false, VFCollisionPair()); - - if (!obs->has_obs()) - return ret; + int n_obs = obs->num_obs(); + if (n_obs==0) { + return std::make_pair(false, VFCollisionPair()); + } - // So I feel bad because we're using the SDF only - // for inside/outside query. Unfortunately this implementation - // doesn't store the face indices within the grid cells, so - // the interpolate function won't return the nearest - // face at the gradient + distance. - Vector3d n; - double dist = obs->sdf.interpolate(0, pt_t1, &n); - if (dist > 0) + for (int i=0; i<n_obs; ++i) + { + if (obs->sdf[i].nCells()==0) { + continue; // not initialized + } + Vector3d n; + double dist = obs->sdf[i].interpolate(0, pt_t1, &n); + if (dist > 0) { continue; } // not colliding + + std::pair<bool,VFCollisionPair> ret = + std::make_pair(true, VFCollisionPair()); + ret.first = true; + ret.second.q_idx = -1; + ret.second.q_is_obs = true; + ret.second.q_bary.setZero(); + ret.second.q_pt = pt_t1 - dist*n; + ret.second.q_n = n.normalized(); return ret; - - ret.first = true; - ret.second.q_idx = -1; - ret.second.q_is_obs = true; - ret.second.q_bary.setZero(); - ret.second.q_pt = pt_t1 - dist*n; - ret.second.q_n = n.normalized(); - return ret; + } + return std::make_pair(false, VFCollisionPair()); } int EmbeddedMeshCollision::detect( @@ -153,17 +182,35 @@ int EmbeddedMeshCollision::detect( const Eigen::MatrixXd *x0, const Eigen::MatrixXd *x1) { - if (!mesh) + if (!mesh) { return 0; + } - if (mesh->type() != MESHTYPE_EMBEDDED) + if (mesh->type() != MESHTYPE_EMBEDDED) { return 0; + } - // Do we even need to process collisions? - if (!this->obsdata.has_obs() && !options->self_collision) - { - if (x1->col(1).minCoeff() > options->floor) - { + // Compute SDFs if the mesh is intersecting + // the associated obstacle. The sdf generation is internally threaded, + // but it might be faster to thread the different SDFs. + bool has_obs_intersection = false; + int n_obs = obsdata.num_obs(); + AlignedBox<double,3> mesh_box = data->col.prim_tree.bounds(); + for (int i=0; i<n_obs; ++i) { + AlignedBox<double,3> &box = obsdata.box[i]; + if (box.isEmpty()) { continue; } + if (!box.intersects(mesh_box)) { continue; } + has_obs_intersection = true; + // Do we need to generate a new SDF? + if (obsdata.sdf[i].nCells()==0) { + obsdata.compute_sdf(i); + } + } + + // Do we even need to process collisions and launch + // the per-vertex threads? + if (!has_obs_intersection && !options->self_collision) { + if (x1->col(2).minCoeff() > options->floor) { return 0; } } @@ -171,8 +218,9 @@ int EmbeddedMeshCollision::detect( // We store the results of the collisions in a per-vertex buffer. // This is a workaround so we can create them in threads. int nev = mesh->rest_facet_verts()->rows(); - if ((int)per_vertex_pairs.size() != nev) + if ((int)per_vertex_pairs.size() != nev) { per_vertex_pairs.resize(nev, std::vector<VFCollisionPair>()); + } // // Thread data for detection @@ -215,6 +263,10 @@ int EmbeddedMeshCollision::detect( Vector3d pt_t0 = td->mesh->get_mapped_facet_vertex(td->x0,vi); Vector3d pt_t1 = td->mesh->get_mapped_facet_vertex(td->x1,vi); +// if (td->options->log_level >= LOGLEVEL_DEBUG) { +// printf("\tDetecting collisions for emb vertex %d: %f %f %f\n", vi, pt_t1[0], pt_t1[1], pt_t1[2]); +// } + // Special case, check if we are below the floor if (pt_t1[2] < td->options->floor) { @@ -231,7 +283,8 @@ int EmbeddedMeshCollision::detect( } // Detect against obstacles - if (td->obsdata->has_obs()) + bool had_obstacle_collision = false; + if (td->obsdata->num_obs()>0) { std::pair<bool,VFCollisionPair> pt_hit_obs = td->collision->detect_against_obs( @@ -247,13 +300,14 @@ int EmbeddedMeshCollision::detect( pt_hit_obs.second.p_is_obs = false; pt_res.emplace_back(vi,vi_pairs.size()); vi_pairs.emplace_back(pt_hit_obs.second); + had_obstacle_collision = true; } } - // We perform self collision if the self_collision flag is true and either: - // a) the set of vertices (vertex group) to do self collision is empty - // b) the vertex is in the set of self collision vertices - bool do_self_collision = td->options->self_collision; + // We perform self collision if the self_collision flag is true and: + // a) there was no obstacle collision + // b) the vertex is in the set of self collision vertices (or its empty) + bool do_self_collision = !had_obstacle_collision && td->options->self_collision; if (do_self_collision) { if (td->data->col.selfcollision_verts.size()>0) { do_self_collision = td->data->col.selfcollision_verts.count(vi)>0; @@ -304,15 +358,24 @@ int EmbeddedMeshCollision::detect( // all of the BVH traversals and the other threads do none. // I haven't actually profiled this, so maybe I'm wrong. int max_threads = std::max(1, std::min(nev, admmpd::get_max_threads(options))); + if (options->log_level >= LOGLEVEL_DEBUG) { + max_threads = 1; + } const auto & per_thread_function = [&per_embedded_vertex_detect,&max_threads,&nev] (DetectThreadData *td, int thread_idx) { - int slice = std::max((int)std::round((nev+1)/double(max_threads)),1); + float slice_f = float(nev+1) / float(max_threads); + int slice = std::max((int)std::ceil(slice_f),1); for (int i=0; i<slice; ++i) { int vi = i*max_threads + thread_idx; - if (vi >= nev) + + // Yeah okay I know this is dumb and I can just do a better job + // of calculating the slice. We can save thread optimization + // for the future, since this will be written different anyway. + if (vi >= nev) { break; + } per_embedded_vertex_detect(td,thread_idx,vi); } @@ -321,8 +384,12 @@ int EmbeddedMeshCollision::detect( // Launch threads std::vector<std::thread> pool; per_thread_results.resize(max_threads, std::vector<Vector2i>()); - for (int i=0; i<max_threads; ++i) + for (int i=0; i<max_threads; ++i) { + per_thread_results[i].reserve(std::max(1,nev/max_threads)); + } + for (int i=0; i<max_threads; ++i) { pool.emplace_back(per_thread_function,&thread_data,i); + } // Combine parallel results vf_pairs.clear(); @@ -439,8 +506,9 @@ EmbeddedMeshCollision::detect_against_self( x1->row(tet[1]), x1->row(tet[2]), x1->row(tet[3])); - if (barys.minCoeff()<-1e-8 || barys.sum() > 1+1e-8) + if (barys.minCoeff()<-1e-8 || barys.sum() > 1+1e-8) { throw std::runtime_error("EmbeddedMeshCollision: Bad tet barys"); + } const MatrixXd *rest_V0 = mesh->rest_prim_verts(); Vector3d rest_pt = @@ -454,8 +522,9 @@ EmbeddedMeshCollision::detect_against_self( if (rest_emb_sdf) { double dist = rest_emb_sdf->interpolate(0, rest_pt); - if (dist > 0) + if (dist > 0) { return ret; // nope + } } // Find triangle surface projection that doesn't @@ -469,23 +538,28 @@ EmbeddedMeshCollision::detect_against_self( skip_tri_inds); mesh->emb_rest_tree()->traverse(nearest_tri); - if (nearest_tri.output.prim<0) + if (nearest_tri.output.prim<0) { throw std::runtime_error("EmbeddedMeshCollision: Failed to find triangle"); + } ret.first = true; ret.second.p_idx = pt_idx; ret.second.p_is_obs = false; ret.second.q_idx = nearest_tri.output.prim; ret.second.q_is_obs = false; - ret.second.q_pt = nearest_tri.output.pt_on_tri; // Compute barycoords of projection RowVector3i f = mesh->facets()->row(nearest_tri.output.prim); Vector3d v3[3] = { emb_V0->row(f[0]), emb_V0->row(f[1]), emb_V0->row(f[2]) }; ret.second.q_bary = geom::point_triangle_barys<double>( nearest_tri.output.pt_on_tri, v3[0], v3[1], v3[2]); - if (ret.second.q_bary.minCoeff()<-1e-8 || ret.second.q_bary.sum() > 1+1e-8) + if (ret.second.q_bary.minCoeff()<-1e-8 || ret.second.q_bary.sum() > 1+1e-8) { throw std::runtime_error("EmbeddedMeshCollision: Bad triangle barys"); + } + + // q_pt is not used for self collisions, but we'll use it + // to define the tet constraint stencil. + ret.second.q_pt = pt_t1; return ret; } @@ -581,7 +655,7 @@ void EmbeddedMeshCollision::linearize( //int nx = x->rows(); d->reserve((int)d->size() + np); trips->reserve((int)trips->size() + np*3*4); - double eta = std::max(0.0,options->collision_thickness); + double eta = 0;//std::max(0.0,options->collision_thickness); for (int i=0; i<np; ++i) { diff --git a/extern/softbody/src/admmpd_collision.h b/extern/softbody/src/admmpd_collision.h index 25d9feb295b..3c9a0a329a5 100644 --- a/extern/softbody/src/admmpd_collision.h +++ b/extern/softbody/src/admmpd_collision.h @@ -23,11 +23,14 @@ struct VFCollisionPair { class Collision { public: struct ObstacleData { - bool has_obs() const { return F.rows()>0; } - void clear(); - Eigen::MatrixXd V; - Eigen::MatrixXi F; - SDFType sdf; + int num_obs() const { return sdf.size(); } + bool compute_sdf(int idx); + std::vector<SDFType> sdf; + // Obstacle data stored in custom matrix type to interop with DiscreGrid + std::vector<Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> > x0; + std::vector<Eigen::Matrix<double,Eigen::Dynamic,3,Eigen::RowMajor> > x1; + std::vector<Eigen::Matrix<unsigned int,Eigen::Dynamic,3,Eigen::RowMajor> > F; + std::vector<Eigen::AlignedBox<double,3> > box; } obsdata; virtual ~Collision() {} @@ -56,15 +59,13 @@ public: const admmpd::Mesh *mesh, std::vector<std::set<int> > &g) = 0; - // Set the soup of obstacles for this time step. - // Returns true on success (SDF generation). - // If err not nullptr, it's set with what caused the error. + // Updates the collision obstacles. If the + // obstacles are new or have moved, the SDF + // is recomputed on the next call to detect(...) virtual bool set_obstacles( - const float *v0, - const float *v1, - int nv, - const unsigned int *faces, - int nf, + std::vector<Eigen::MatrixXd> &v0, + std::vector<Eigen::MatrixXd> &v1, + std::vector<Eigen::MatrixXi> &F, std::string *err=nullptr); // Linearizes active collision pairs about x diff --git a/extern/softbody/src/admmpd_linsolve.cpp b/extern/softbody/src/admmpd_linsolve.cpp index bd06f5910a7..6313393d232 100644 --- a/extern/softbody/src/admmpd_linsolve.cpp +++ b/extern/softbody/src/admmpd_linsolve.cpp @@ -74,14 +74,17 @@ void LDLT::init_solve( data->ls.Ptq = options->pk * P.transpose() * q; } - if (!data->ls.ldlt_A_PtP) + bool new_ptr = false; + if (!data->ls.ldlt_A_PtP) { data->ls.ldlt_A_PtP = std::make_unique<Cholesky>(); + new_ptr = true; + } // Compute A + P'P and factorize: // 1) A not computed // 2) P has changed // 3) factorization not set - if ( data->ls.ldlt_A_PtP->info() != Eigen::Success || + if ( new_ptr || data->ls.A_PtP.nonZeros()==0 || new_P) { diff --git a/extern/softbody/src/admmpd_log.cpp b/extern/softbody/src/admmpd_log.cpp index 28c196928a6..c979cbd8ef5 100644 --- a/extern/softbody/src/admmpd_log.cpp +++ b/extern/softbody/src/admmpd_log.cpp @@ -56,6 +56,7 @@ std::string Logger::state_string(int state) { default: break; case SOLVERSTATE_INIT: str="init"; break; + case SOLVERSTATE_MESHCREATE: str="mesh_init"; break; case SOLVERSTATE_SOLVE: str="solve"; break; case SOLVERSTATE_INIT_SOLVE: str="init_solve"; break; case SOLVERSTATE_LOCAL_STEP: str="local_step"; break; diff --git a/extern/softbody/src/admmpd_mesh.cpp b/extern/softbody/src/admmpd_mesh.cpp index 4afa2f5c228..6e9884ca4c9 100644 --- a/extern/softbody/src/admmpd_mesh.cpp +++ b/extern/softbody/src/admmpd_mesh.cpp @@ -4,6 +4,7 @@ #include "admmpd_mesh.h" #include "admmpd_geom.h" #include "admmpd_timer.h" +#include "admmpd_log.h" #include <unordered_map> #include <set> #include <iostream> @@ -62,6 +63,7 @@ static void gather_octree_tets( } // end gather octree tets bool EmbeddedMesh::create( + const Options *options, const float *verts, // size nv*3 int nv, const unsigned int *faces, // size nf*3 @@ -69,11 +71,20 @@ bool EmbeddedMesh::create( const unsigned int *tets, // ignored int nt) { + admmpd::Logger log(options->log_level); + log.start_state(SOLVERSTATE_MESHCREATE); + P_updated = true; - if (nv<=0 || verts == nullptr) + mesh_is_closed = false; + + if (nv<=0 || verts == nullptr) { + log.stop_state(SOLVERSTATE_MESHCREATE); return false; - if (nf<=0 || faces == nullptr) + } + if (nf<=0 || faces == nullptr) { + log.stop_state(SOLVERSTATE_MESHCREATE); return false; + } (void)(tets); (void)(nt); @@ -153,8 +164,12 @@ bool EmbeddedMesh::create( compute_sdf(&emb_V0, &emb_F, &emb_sdf); emb_rest_facet_tree.init(emb_leaves); - compute_lattice(); - compute_embedding(); + if (!compute_lattice(options)) { + throw_err("create","Failed lattice generation"); + } + if (!compute_embedding(options)) { + throw_err("create","Failed embedding calculation"); + } // Verify embedding is correct for (int i=0; i<nv; ++i) @@ -179,14 +194,23 @@ bool EmbeddedMesh::create( if (emb_V0.rows()==0) throw_err("create","Did not set verts"); + log.stop_state(SOLVERSTATE_MESHCREATE); + if (options->log_level >= LOGLEVEL_DEBUG) { + printf("%s\n",log.to_string().c_str()); + } + return true; } void EmbeddedMesh::compute_sdf( const Eigen::MatrixXd *emb_v, const Eigen::MatrixXi *emb_f, - SDFType *sdf) const + SDFType *sdf) { + if (emb_f->rows()==0) { + return; + } + Matrix<double,Dynamic,Dynamic,RowMajor> v_rm = *emb_v; Matrix<unsigned int,Dynamic,Dynamic,RowMajor> f_rm = emb_f->cast<unsigned int>(); @@ -198,10 +222,16 @@ void EmbeddedMesh::compute_sdf( domain.max() += 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones(); domain.min() -= 1e-3 * domain.diagonal().norm() * Eigen::Vector3d::Ones(); + // Decide an SDF resolution. We want it to scale: high resolution + // for lower resolution meshes. This is because it becomes WAY too + // expensive to compute SDFs for high res meshes. + int nf = emb_f->rows(); + unsigned int res = std::max(10, 20-(int)std::log(nf)); + std::array<unsigned int, 3> resolution; + resolution[0] = res; resolution[1] = res; resolution[2] = res; Discregrid::TriangleMesh tm(v_rm.data(), f_rm.data(), v_rm.rows(), f_rm.rows()); + mesh_is_closed = tm.is_closed(); Discregrid::MeshDistance md(tm); - std::array<unsigned int, 3> resolution; - resolution[0] = 30; resolution[1] = 30; resolution[2] = 30; *sdf = Discregrid::CubicLagrangeDiscreteGrid(domain, resolution); auto func = Discregrid::DiscreteGrid::ContinuousFunction{}; std::vector<std::thread::id> thread_map; @@ -212,7 +242,7 @@ void EmbeddedMesh::compute_sdf( sdf->addFunction(func, &thread_map, false); } -bool EmbeddedMesh::compute_lattice() +bool EmbeddedMesh::compute_lattice(const admmpd::Options *options) { // Create subset of faces // if (emb_faces.size()==0) { return false; } @@ -228,7 +258,7 @@ bool EmbeddedMesh::compute_lattice() // Create an octree to generate the tets from Octree<double,3> octree; - octree.init(&emb_V0,&F,options.max_subdiv_levels); + octree.init(&emb_V0,&F,options->lattice_subdiv); std::vector<Vector3d> lat_verts; std::vector<RowVector4i> lat_tets; @@ -258,9 +288,10 @@ bool EmbeddedMesh::compute_lattice() return nlt>0; } -bool EmbeddedMesh::compute_embedding() +bool EmbeddedMesh::compute_embedding(const admmpd::Options *options) { struct FindTetThreadData { + const Options *opt; AABBTree<double,3> *tree; EmbeddedMesh *emb_mesh; // thread sets vtx_to_tet and barys MatrixXd *lat_V0; @@ -269,6 +300,10 @@ bool EmbeddedMesh::compute_embedding() VectorXi *emb_v_to_tet; }; + if (options->log_level >= LOGLEVEL_DEBUG) { + printf("Computing embedding for %d verts\n", (int)emb_V0.rows()); + } + auto parallel_point_in_tet = []( void *__restrict userdata, const int i, @@ -297,6 +332,15 @@ bool EmbeddedMesh::compute_embedding() td->emb_v_to_tet->operator[](i) = tet_idx; Vector4d b = geom::point_tet_barys<double>(pt,t[0],t[1],t[2],t[3]); td->emb_barys->row(i) = b; + + if (td->opt->log_level >= LOGLEVEL_DEBUG) { + printf("\tFound embedding for %d: %f %f %f %f\n", i, b[0],b[1],b[2],b[3]); + } + } + else { + if (td->opt->log_level >= LOGLEVEL_DEBUG) { + printf("\tDid NOT find embedding for %d!\n", i); + } } }; // end parallel find tet @@ -309,14 +353,15 @@ bool EmbeddedMesh::compute_embedding() emb_barys.resize(nv,4); emb_barys.setZero(); emb_v_to_tet.resize(nv); - emb_v_to_tet.array() = -1; + emb_v_to_tet.setOnes(); + emb_v_to_tet *= -1; int nt = lat_T.rows(); // BVH tree for finding point-in-tet and computing // barycoords for each embedded vertex std::vector<AlignedBox<double,3> > tet_aabbs; tet_aabbs.resize(nt); - Vector3d veta = Vector3d::Ones()*1e-12; + Vector3d veta = Vector3d::Ones()*1e-4; for (int i=0; i<nt; ++i) { tet_aabbs[i].setEmpty(); @@ -332,6 +377,7 @@ bool EmbeddedMesh::compute_embedding() tree.init(tet_aabbs); FindTetThreadData thread_data = { + options, &tree, this, &lat_V0, @@ -341,6 +387,9 @@ bool EmbeddedMesh::compute_embedding() }; TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); + if (options->log_level >= LOGLEVEL_DEBUG) { + settings.use_threading = false; + } BLI_task_parallel_range(0, nv, &thread_data, parallel_point_in_tet, &settings); // Double check we set (valid) barycoords for every embedded vertex @@ -497,6 +546,7 @@ bool EmbeddedMesh::linearize_pins( } bool TetMesh::create( + const Options *options, const float *verts, // size nv*3 int nv, const unsigned int *faces, // size nf*3 (surface faces) @@ -505,6 +555,7 @@ bool TetMesh::create( int nt) // must be > 0 { P_updated = true; + if (nv<=0 || verts == nullptr) return false; if (nf<=0 || faces == nullptr) @@ -664,6 +715,7 @@ bool TetMesh::linearize_pins( } bool TriangleMesh::create( + const Options *options, const float *verts, int nv, const unsigned int *faces, @@ -671,7 +723,9 @@ bool TriangleMesh::create( const unsigned int *tets, int nt) { + (void)(options); P_updated = true; + (void)(tets); (void)(nt); if (nv<=0 || verts == nullptr) return false; diff --git a/extern/softbody/src/admmpd_mesh.h b/extern/softbody/src/admmpd_mesh.h index 364355a8a06..01e0f644418 100644 --- a/extern/softbody/src/admmpd_mesh.h +++ b/extern/softbody/src/admmpd_mesh.h @@ -18,6 +18,7 @@ public: // Copy buffers to internal data, // calculates BVH/SDF, etc... virtual bool create( + const Options *options, const float *verts, // size nv*3 int nv, const unsigned int *faces, // size nf*3 @@ -35,6 +36,8 @@ public: virtual const Eigen::MatrixXd *rest_facet_verts() const = 0; virtual const SDFType *rest_facet_sdf() const = 0; + virtual bool self_collision_allowed() const = 0; + // Maps primitive vertex to facet vertex. For standard tet meshes // it's just one-to-one, but embedded meshes use bary weighting. virtual Eigen::Vector3d get_mapped_facet_vertex( @@ -85,32 +88,27 @@ protected: std::unordered_map<int,double> emb_pin_k; std::unordered_map<int,Eigen::Vector3d> emb_pin_pos; admmpd::AABBTree<double,3> emb_rest_facet_tree; + bool mesh_is_closed; SDFType emb_sdf; mutable bool P_updated; // set to false on linearize_pins - bool compute_embedding(); + bool compute_embedding(const admmpd::Options *options); // Computes the tet mesh on a subset of faces - bool compute_lattice(); + bool compute_lattice(const admmpd::Options *options); + // Sets mesh_is_closed void compute_sdf( const Eigen::MatrixXd *emb_v, const Eigen::MatrixXi *emb_f, - SDFType *sdf) const; + SDFType *sdf); public: int type() const { return MESHTYPE_EMBEDDED; } - struct Options - { - int max_subdiv_levels; - Options() : - max_subdiv_levels(3) - {} - } options; - bool create( + const Options *options, const float *verts, // size nv*3 int nv, const unsigned int *faces, // size nf*3 @@ -127,6 +125,8 @@ public: const SDFType *rest_facet_sdf() const { return &emb_sdf; } const admmpd::AABBTree<double,3> *emb_rest_tree() const { return &emb_rest_facet_tree; } + bool self_collision_allowed() const { return mesh_is_closed; } + Eigen::Vector3d get_mapped_facet_vertex( const Eigen::MatrixXd *prim_verts, int facet_vertex_idx) const; @@ -177,6 +177,7 @@ public: int type() const { return MESHTYPE_TET; } bool create( + const Options *options, const float *verts, // size nv*3 int nv, const unsigned int *faces, // size nf*3 (surface faces) @@ -190,6 +191,9 @@ public: const Eigen::MatrixXd *rest_prim_verts() const { return &V0; } const SDFType *rest_facet_sdf() const { return &rest_sdf; } + // Not yet implemented + bool self_collision_allowed() const { return false; } + Eigen::Vector3d get_mapped_facet_vertex( const Eigen::MatrixXd *prim_verts, int facet_vertex_idx) const @@ -237,6 +241,7 @@ public: int type() const { return MESHTYPE_TRIANGLE; } bool create( + const Options *options, const float *verts, // size nv*3 int nv, const unsigned int *faces, // size nf*3 @@ -250,6 +255,9 @@ public: const Eigen::MatrixXd *rest_facet_verts() const { return &V0; } const SDFType *rest_facet_sdf() const { return nullptr; } + // Not yet implemented + bool self_collision_allowed() const { return false; } + Eigen::Vector3d get_mapped_facet_vertex( const Eigen::MatrixXd *prim_verts, int facet_vertex_idx) const { diff --git a/extern/softbody/src/admmpd_types.h b/extern/softbody/src/admmpd_types.h index 85dbc1f77b3..5e512fb1dce 100644 --- a/extern/softbody/src/admmpd_types.h +++ b/extern/softbody/src/admmpd_types.h @@ -36,13 +36,14 @@ typedef Discregrid::CubicLagrangeDiscreteGrid SDFType; #define COLLISIONMODE_NUM 2 #define SOLVERSTATE_INIT 0 -#define SOLVERSTATE_SOLVE 1 -#define SOLVERSTATE_INIT_SOLVE 2 -#define SOLVERSTATE_LOCAL_STEP 3 -#define SOLVERSTATE_GLOBAL_STEP 4 -#define SOLVERSTATE_COLLISION_UPDATE 5 -#define SOLVERSTATE_TEST_CONVERGED 6 -#define SOLVERSTATE_NUM 7 +#define SOLVERSTATE_MESHCREATE 1 +#define SOLVERSTATE_SOLVE 2 +#define SOLVERSTATE_INIT_SOLVE 3 +#define SOLVERSTATE_LOCAL_STEP 4 +#define SOLVERSTATE_GLOBAL_STEP 5 +#define SOLVERSTATE_COLLISION_UPDATE 6 +#define SOLVERSTATE_TEST_CONVERGED 7 +#define SOLVERSTATE_NUM 8 #define LOGLEVEL_NONE 0 #define LOGLEVEL_LOW 1 @@ -57,6 +58,7 @@ typedef Discregrid::CubicLagrangeDiscreteGrid SDFType; struct Options { double timestep_s; + int lattice_subdiv; // max subdiv levels for lattice gen int log_level; int linsolver; int max_admm_iters; @@ -74,12 +76,13 @@ struct Options { double poisson; // Poisson ratio // TODO variable per-tet double density_kgm3; // density of mesh double floor; // floor location - double collision_thickness; + //double collision_thickness; bool self_collision; // process self collisions Eigen::Vector2d strain_limit; // min=[-inf,1], max=[1,inf] Eigen::Vector3d grav; Options() : timestep_s(1.0/24.0), + lattice_subdiv(3), log_level(LOGLEVEL_NONE), linsolver(LINSOLVER_PCG), max_admm_iters(20), @@ -97,7 +100,7 @@ struct Options { poisson(0.399), density_kgm3(1522), floor(-std::numeric_limits<double>::max()), - collision_thickness(1e-6), + //collision_thickness(1e-6), self_collision(false), strain_limit(0,100), grav(0,0,-9.8) diff --git a/intern/clog/CLG_log.h b/intern/clog/CLG_log.h index 7418623755c..a2841c5c8b3 100644 --- a/intern/clog/CLG_log.h +++ b/intern/clog/CLG_log.h @@ -132,13 +132,14 @@ void CLG_logf(CLG_LogType *lg, const char *format, ...) _CLOG_ATTR_NONNULL(1, 3, 4, 5) _CLOG_ATTR_PRINTF_FORMAT(5, 6); -/* Main initializer and distructor (per session, not logger). */ +/* Main initializer and destructor (per session, not logger). */ void CLG_init(void); void CLG_exit(void); void CLG_output_set(void *file_handle); void CLG_output_use_basename_set(int value); void CLG_output_use_timestamp_set(int value); +void CLG_error_fn_set(void (*error_fn)(void *file_handle)); void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle)); void CLG_backtrace_fn_set(void (*fatal_fn)(void *file_handle)); diff --git a/intern/clog/clog.c b/intern/clog/clog.c index d384b9a89e6..84b850f5042 100644 --- a/intern/clog/clog.c +++ b/intern/clog/clog.c @@ -98,6 +98,7 @@ typedef struct CLogContext { } default_type; struct { + void (*error_fn)(void *file_handle); void (*fatal_fn)(void *file_handle); void (*backtrace_fn)(void *file_handle); } callbacks; @@ -352,6 +353,13 @@ static CLG_LogType *clg_ctx_type_register(CLogContext *ctx, const char *identifi return ty; } +static void clg_ctx_error_action(CLogContext *ctx) +{ + if (ctx->callbacks.error_fn != NULL) { + ctx->callbacks.error_fn(ctx->output_file); + } +} + static void clg_ctx_fatal_action(CLogContext *ctx) { if (ctx->callbacks.fatal_fn != NULL) { @@ -522,6 +530,10 @@ void CLG_logf(CLG_LogType *lg, clg_ctx_backtrace(lg->ctx); } + if (severity == CLG_SEVERITY_ERROR) { + clg_ctx_error_action(lg->ctx); + } + if (severity == CLG_SEVERITY_FATAL) { clg_ctx_fatal_action(lg->ctx); } @@ -555,6 +567,12 @@ static void CLG_ctx_output_use_timestamp_set(CLogContext *ctx, int value) } } +/** Action on error severity. */ +static void CLT_ctx_error_fn_set(CLogContext *ctx, void (*error_fn)(void *file_handle)) +{ + ctx->callbacks.error_fn = error_fn; +} + /** Action on fatal severity. */ static void CLG_ctx_fatal_fn_set(CLogContext *ctx, void (*fatal_fn)(void *file_handle)) { @@ -674,6 +692,11 @@ void CLG_output_use_timestamp_set(int value) CLG_ctx_output_use_timestamp_set(g_ctx, value); } +void CLG_error_fn_set(void (*error_fn)(void *file_handle)) +{ + CLT_ctx_error_fn_set(g_ctx, error_fn); +} + void CLG_fatal_fn_set(void (*fatal_fn)(void *file_handle)) { CLG_ctx_fatal_fn_set(g_ctx, fatal_fn); diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index f4354d5166e..e40e1f5f001 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -923,48 +923,34 @@ static void create_subd_mesh(Scene *scene, /* Sync */ -static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob, - BL::Scene /*b_scene*/) +static BL::MeshSequenceCacheModifier object_mesh_cache_find(BL::Object &b_ob) { - BL::Object::modifiers_iterator b_mod; + if (b_ob.modifiers.length() > 0) { + BL::Modifier b_mod = b_ob.modifiers[b_ob.modifiers.length() - 1]; - for (b_ob.modifiers.begin(b_mod); b_mod != b_ob.modifiers.end(); ++b_mod) { - if (!b_mod->is_a(&RNA_MeshSequenceCacheModifier)) { - continue; - } - - BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(*b_mod); + if (b_mod.type() == BL::Modifier::type_MESH_SEQUENCE_CACHE) { + BL::MeshSequenceCacheModifier mesh_cache = BL::MeshSequenceCacheModifier(b_mod); - if (MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) { - return mesh_cache; + if (MeshSequenceCacheModifier_has_velocity_get(&mesh_cache.ptr)) { + return mesh_cache; + } } } return BL::MeshSequenceCacheModifier(PointerRNA_NULL); } -static void sync_mesh_cached_velocities(BL::Object &b_ob, - BL::Scene b_scene, - Scene *scene, - Mesh *mesh) +static void sync_mesh_cached_velocities(BL::Object &b_ob, Scene *scene, Mesh *mesh) { if (scene->need_motion() == Scene::MOTION_NONE) return; - BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob, b_scene); + BL::MeshSequenceCacheModifier b_mesh_cache = object_mesh_cache_find(b_ob); if (!b_mesh_cache) { return; } - /* Find or add attribute */ - float3 *P = &mesh->verts[0]; - Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - - if (!attr_mP) { - attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); - } - if (!MeshSequenceCacheModifier_read_velocity_get(&b_mesh_cache.ptr)) { return; } @@ -975,6 +961,14 @@ static void sync_mesh_cached_velocities(BL::Object &b_ob, return; } + /* Find or add attribute */ + float3 *P = &mesh->verts[0]; + Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + + if (!attr_mP) { + attr_mP = mesh->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); + } + /* Only export previous and next frame, we don't have any in between data. */ float motion_times[2] = {-1.0f, 1.0f}; for (int step = 0; step < 2; step++) { @@ -1071,7 +1065,7 @@ void BlenderSync::sync_mesh(BL::Depsgraph b_depsgraph, } /* cached velocities (e.g. from alembic archive) */ - sync_mesh_cached_velocities(b_ob, b_depsgraph.scene(), scene, mesh); + sync_mesh_cached_velocities(b_ob, scene, mesh); /* mesh fluid motion mantaflow */ sync_mesh_fluid_motion(b_ob, scene, mesh); @@ -1095,7 +1089,7 @@ void BlenderSync::sync_mesh_motion(BL::Depsgraph b_depsgraph, } /* Cached motion blur already exported. */ - BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob, b_scene); + BL::MeshSequenceCacheModifier mesh_cache = object_mesh_cache_find(b_ob); if (mesh_cache) { return; } diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index fb704b2a24a..bf9fc784d79 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -363,7 +363,8 @@ void BlenderSession::do_write_update_render_tile(RenderTile &rtile, PassType pass_type = BlenderSync::get_pass_type(b_pass); int components = b_pass.channels(); - rtile.buffers->set_pass_rect(pass_type, components, (float *)b_pass.rect()); + rtile.buffers->set_pass_rect( + pass_type, components, (float *)b_pass.rect(), rtile.num_samples); } end_render_result(b_engine, b_rr, false, false, false); diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index ca366722eb7..466872e0557 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -67,6 +67,7 @@ set(LIB cycles_render cycles_kernel cycles_util + ${BLENDER_GL_LIBRARIES} ) if(WITH_CUDA_DYNLOAD) diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp index e851749949d..f0683d12f1f 100644 --- a/intern/cycles/device/opencl/device_opencl_impl.cpp +++ b/intern/cycles/device/opencl/device_opencl_impl.cpp @@ -864,6 +864,11 @@ void OpenCLDevice::load_preview_kernels() bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requested_features) { + if (requested_features.use_baking) { + /* For baking, kernels have already been loaded in load_required_kernels(). */ + return true; + } + if (background) { load_kernel_task_pool.wait_work(); use_preview_kernels = false; @@ -1933,13 +1938,12 @@ void OpenCLDevice::bake(DeviceTask &task, RenderTile &rtile) kernel_set_args(kernel, start_arg_index, sample); enqueue_kernel(kernel, d_w, d_h); + clFinish(cqCommandQueue); rtile.sample = sample + 1; task.update_progress(&rtile, rtile.w * rtile.h); } - - clFinish(cqCommandQueue); } static bool kernel_build_opencl_2(cl_device_id cdDevice) diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h index 8344f4b4f47..17b69b6198b 100644 --- a/intern/cycles/kernel/kernel_film.h +++ b/intern/cycles/kernel/kernel_film.h @@ -47,7 +47,7 @@ ccl_device float4 film_get_pass_result(KernelGlobals *kg, if (kernel_data.film.use_display_exposure) { float exposure = kernel_data.film.exposure; - pass_result *= make_float4(exposure, exposure, exposure, alpha); + pass_result *= make_float4(exposure, exposure, exposure, 1.0f); } } else if (display_pass_components == 1) { diff --git a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl b/intern/cycles/kernel/kernels/opencl/kernel_bake.cl index 041312b53cb..7b81e387467 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl +++ b/intern/cycles/kernel/kernels/opencl/kernel_bake.cl @@ -12,12 +12,11 @@ __kernel void kernel_ocl_bake( ccl_constant KernelData *data, - ccl_global uint4 *input, - ccl_global float4 *output, + ccl_global float *buffer, KERNEL_BUFFER_PARAMS, - int type, int filter, int sx, int sw, int offset, int sample) + int sx, int sy, int sw, int sh, int offset, int stride, int sample) { KernelGlobals kglobals, *kg = &kglobals; @@ -27,12 +26,11 @@ __kernel void kernel_ocl_bake( kernel_set_buffer_info(kg); int x = sx + ccl_global_id(0); + int y = sy + ccl_global_id(1); - if(x < sx + sw) { -#ifdef __NO_BAKING__ - output[x] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); -#else - kernel_bake_evaluate(kg, input, output, (ShaderEvalType)type, filter, x, offset, sample); + if(x < sx + sw && y < sy + sh) { +#ifndef __NO_BAKING__ + kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride); #endif } } diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/osl_globals.h index c06c9abd4c1..caca3c28c8d 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/osl_globals.h @@ -90,6 +90,7 @@ struct OSLTraceData { ShaderData sd; bool setup; bool init; + bool hit; }; /* thread key for thread specific data lookup */ diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/osl_services.cpp index 5292b5f8055..aee1e3a244e 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/osl_services.cpp @@ -1481,6 +1481,7 @@ bool OSLRenderServices::trace(TraceOpt &options, tracedata->ray = ray; tracedata->setup = false; tracedata->init = true; + tracedata->hit = false; tracedata->sd.osl_globals = sd->osl_globals; KernelGlobals *kg = sd->osl_globals; @@ -1492,7 +1493,8 @@ bool OSLRenderServices::trace(TraceOpt &options, /* Raytrace, leaving out shadow opaque to avoid early exit. */ uint visibility = PATH_RAY_ALL_VISIBILITY - PATH_RAY_SHADOW_OPAQUE; - return scene_intersect(kg, &ray, visibility, &tracedata->isect); + tracedata->hit = scene_intersect(kg, &ray, visibility, &tracedata->isect); + return tracedata->hit; } bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg, @@ -1506,9 +1508,9 @@ bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg, if (source == u_trace && tracedata->init) { if (name == u_hit) { - return set_attribute_int((tracedata->isect.prim != PRIM_NONE), type, derivatives, val); + return set_attribute_int(tracedata->hit, type, derivatives, val); } - else if (tracedata->isect.prim != PRIM_NONE) { + else if (tracedata->hit) { if (name == u_hitdist) { float f[3] = {tracedata->isect.t, 0.0f, 0.0f}; return set_attribute_float(f, type, derivatives, val); diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp index b26366af852..3607300cee6 100644 --- a/intern/cycles/render/buffers.cpp +++ b/intern/cycles/render/buffers.cpp @@ -459,7 +459,7 @@ bool RenderBuffers::get_pass_rect( return false; } -bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels) +bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels, int samples) { if (buffer.data() == NULL) { return false; @@ -482,8 +482,17 @@ bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels) assert(pass.components == components); for (int i = 0; i < size; i++, out += pass_stride, pixels += components) { - for (int j = 0; j < components; j++) { - out[j] = pixels[j]; + if (pass.filter) { + /* Scale by the number of samples, inverse of what we do in get_pass_rect. + * A better solution would be to remove the need for set_pass_rect entirely, + * and change baking to bake multiple objects in a tile at once. */ + for (int j = 0; j < components; j++) { + out[j] = pixels[j] * samples; + } + } + else { + /* For non-filtered passes just straight copy, these may contain non-float data. */ + memcpy(out, pixels, sizeof(float) * components); } } diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h index 06b6094e6c9..425400a2c08 100644 --- a/intern/cycles/render/buffers.h +++ b/intern/cycles/render/buffers.h @@ -92,7 +92,7 @@ class RenderBuffers { const string &name, float exposure, int sample, int components, float *pixels); bool get_denoising_pass_rect( int offset, float exposure, int sample, int components, float *pixels); - bool set_pass_rect(PassType type, int components, float *pixels); + bool set_pass_rect(PassType type, int components, float *pixels, int samples); }; /* Display Buffer diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp index 7072fff4892..2da28222a7f 100644 --- a/intern/cycles/render/film.cpp +++ b/intern/cycles/render/film.cpp @@ -253,6 +253,8 @@ void Pass::add(PassType type, vector<Pass> &passes, const char *name) case PASS_BAKE_PRIMITIVE: case PASS_BAKE_DIFFERENTIAL: pass.components = 4; + pass.exposure = false; + pass.filter = false; break; default: assert(false); diff --git a/intern/ghost/intern/GHOST_WindowCocoa.mm b/intern/ghost/intern/GHOST_WindowCocoa.mm index 62b0e48a7c1..f8e2f96d111 100644 --- a/intern/ghost/intern/GHOST_WindowCocoa.mm +++ b/intern/ghost/intern/GHOST_WindowCocoa.mm @@ -415,7 +415,7 @@ GHOST_WindowCocoa::GHOST_WindowCocoa(GHOST_SystemCocoa *systemCocoa, [parentWindow->getCocoaWindow() addChildWindow:m_window ordered:NSWindowAbove]; [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; } - else if (state != GHOST_kWindowStateFullScreen) { + else { [m_window setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; } diff --git a/intern/guardedalloc/MEM_guardedalloc.h b/intern/guardedalloc/MEM_guardedalloc.h index 9c62b2396f6..c05bda030ad 100644 --- a/intern/guardedalloc/MEM_guardedalloc.h +++ b/intern/guardedalloc/MEM_guardedalloc.h @@ -215,6 +215,11 @@ extern const char *(*MEM_name_ptr)(void *vmemh); * about memory leaks will be printed on exit. */ void MEM_init_memleak_detection(void); +/** When this has been called and memory leaks have been detected, the process will have an exit + * code that indicates failure. This can be used for when checking for memory leaks with automated + * tests. */ +void MEM_enable_fail_on_memleak(void); + /* Switch allocator to slower but fully guarded mode. */ void MEM_use_guarded_allocator(void); diff --git a/intern/guardedalloc/intern/leak_detector.cc b/intern/guardedalloc/intern/leak_detector.cc index d7b6f749742..0ecf2ed8ba7 100644 --- a/intern/guardedalloc/intern/leak_detector.cc +++ b/intern/guardedalloc/intern/leak_detector.cc @@ -18,6 +18,8 @@ * \ingroup MEM */ +#include <cstdlib> + #include "MEM_guardedalloc.h" #include "mallocn_intern.h" @@ -28,6 +30,9 @@ char free_after_leak_detection_message[] = "error, use the 'construct on first use' idiom."; namespace { + +bool fail_on_memleak = false; + class MemLeakPrinter { public: ~MemLeakPrinter() @@ -42,6 +47,15 @@ class MemLeakPrinter { leaked_blocks, (double)mem_in_use / 1024 / 1024); MEM_printmemlist(); + + if (fail_on_memleak) { + /* There are many other ways to change the exit code to failure here: + * - Make the destructor noexcept(false) and throw an exception. + * - Call exit(EXIT_FAILURE). + * - Call terminate(). + */ + abort(); + } } }; } // namespace @@ -59,3 +73,8 @@ void MEM_init_memleak_detection(void) */ static MemLeakPrinter printer; } + +void MEM_enable_fail_on_memleak(void) +{ + fail_on_memleak = true; +} diff --git a/intern/mantaflow/intern/manta_fluid_API.cpp b/intern/mantaflow/intern/manta_fluid_API.cpp index 530dbd49b7c..7f96a315a8e 100644 --- a/intern/mantaflow/intern/manta_fluid_API.cpp +++ b/intern/mantaflow/intern/manta_fluid_API.cpp @@ -456,7 +456,7 @@ int manta_smoke_ensure_fire(MANTA *smoke, struct FluidModifierData *fmd) if (!smoke || !fmd) return 0; - int result = smoke->initFire(fmd); + bool result = smoke->initFire(fmd); if (smoke->usingNoise()) { result &= smoke->initFireHigh(fmd); } @@ -468,7 +468,7 @@ int manta_smoke_ensure_colors(MANTA *smoke, struct FluidModifierData *fmd) if (!smoke || !fmd) return 0; - int result = smoke->initColors(fmd); + bool result = smoke->initColors(fmd); if (smoke->usingNoise()) { result &= smoke->initColorsHigh(fmd); } diff --git a/intern/numaapi/source/build_config.h b/intern/numaapi/source/build_config.h index c6392532914..8e351f1c718 100644 --- a/intern/numaapi/source/build_config.h +++ b/intern/numaapi/source/build_config.h @@ -96,7 +96,7 @@ #elif defined(__QNXNTO__) # define OS_QNX 1 #elif defined(__asmjs__) || defined(__wasm__) -# define OS_ASMJS +# define OS_ASMJS 1 #else # error Please add support for your platform in build_config.h #endif diff --git a/intern/softbody/admmpd_api.cpp b/intern/softbody/admmpd_api.cpp index 85d4718bf7f..a9699eb936b 100644 --- a/intern/softbody/admmpd_api.cpp +++ b/intern/softbody/admmpd_api.cpp @@ -31,32 +31,18 @@ #endif #include "DNA_mesh_types.h" // Mesh #include "DNA_meshdata_types.h" // MVert +#include "DNA_modifier_types.h" // CollisionModifierData #include "DNA_object_force_types.h" // Enums #include "BKE_mesh.h" // BKE_mesh_free #include "BKE_softbody.h" // BodyPoint #include "BKE_deform.h" // BKE_defvert_find_index +#include "BKE_modifier.h" // BKE_modifiers_findby_type #include "MEM_guardedalloc.h" #include <iostream> #include <memory> #include <algorithm> -// Collision obstacles are cached until -// solve(...) is called. If we are substepping, -// the obstacle is interpolated from start to end. -struct CollisionObstacle -{ - Eigen::VectorXf x0, x1; - std::vector<unsigned int> F; - bool needs_sdf_recompute; - void reset() { - x0 = Eigen::VectorXf(); - x1 = Eigen::VectorXf(); - F.clear(); - needs_sdf_recompute = true; - } -}; - struct ADMMPDInternalData { // Created in admmpd_update_mesh @@ -66,7 +52,8 @@ struct ADMMPDInternalData std::shared_ptr<admmpd::Options> options; std::shared_ptr<admmpd::SolverData> data; // Created in set_obstacles - CollisionObstacle obs; + std::vector<Eigen::MatrixXd> obs_x0, obs_x1; + std::vector<Eigen::MatrixXi> obs_F; }; @@ -104,6 +91,7 @@ static inline void options_from_object( op->linsolver = std::max(0, std::min(LINSOLVER_NUM-1, sb->admmpd_linsolver)); op->strain_limit[0] = std::min(1.f, sb->admmpd_strainlimit_min); op->strain_limit[1] = std::max(1.f, sb->admmpd_strainlimit_max); + op->lattice_subdiv = std::max(1,sb->admmpd_embed_res); if (!skip_require_reset) { @@ -186,10 +174,8 @@ static inline int admmpd_init_with_lattice(ADMMPDInterfaceData *iface, Object *o std::vector<unsigned int> f; vecs_from_object(ob,vertexCos,v,f); iface->idata->mesh = std::make_shared<admmpd::EmbeddedMesh>(); - - admmpd::EmbeddedMesh* emb_msh = static_cast<admmpd::EmbeddedMesh*>(iface->idata->mesh.get()); - emb_msh->options.max_subdiv_levels = ob->soft->admmpd_embed_res; bool success = iface->idata->mesh->create( + iface->idata->options.get(), v.data(), v.size()/3, f.data(), @@ -197,7 +183,7 @@ static inline int admmpd_init_with_lattice(ADMMPDInterfaceData *iface, Object *o nullptr, 0); - if (!success) { + if (!success) { // soft unknown fail strcpy_error(iface, "EmbeddedMesh failed on creation"); return 0; } @@ -214,6 +200,7 @@ static inline int admmpd_init_as_cloth(ADMMPDInterfaceData *iface, Object *ob, f iface->idata->mesh = std::make_shared<admmpd::TriangleMesh>(); bool success = iface->idata->mesh->create( + iface->idata->options.get(), v.data(), v.size()/3, f.data(), @@ -230,6 +217,53 @@ static inline int admmpd_init_as_cloth(ADMMPDInterfaceData *iface, Object *ob, f return 1; } +void admmpd_compute_lattice( + int subdiv, + float *in_verts, int in_nv, + unsigned int *in_faces, int in_nf, + float **out_verts, int *out_nv, + unsigned int **out_tets, int *out_nt) +{ + + admmpd::EmbeddedMesh emesh; + admmpd::Options opt; + opt.lattice_subdiv = subdiv; + bool success = emesh.create( + &opt, + in_verts, in_nv, + in_faces, in_nf, + nullptr, + 0); + + if (!success) { + return; + } + + const Eigen::MatrixXd &vt = *emesh.rest_prim_verts(); + const Eigen::MatrixXi &t = *emesh.prims(); + if (vt.rows()==0 || t.rows()==0) { + return; + } + + *out_nv = vt.rows(); + *out_verts = (float*)MEM_callocN(sizeof(float)*3*(vt.rows()), "ADMMPD_lattice_verts"); + *out_nt = t.rows(); + *out_tets = (unsigned int*)MEM_callocN(sizeof(unsigned int)*4*(t.rows()), "ADMMPD_lattice_tets"); + + for (int i=0; i<vt.rows(); ++i) { + (*out_verts)[i*3+0] = vt(i,0); + (*out_verts)[i*3+1] = vt(i,1); + (*out_verts)[i*3+2] = vt(i,2); + } + + for (int i=0; i<t.rows(); ++i) { + (*out_tets)[i*4+0] = t(i,0); + (*out_tets)[i*4+1] = t(i,1); + (*out_tets)[i*4+2] = t(i,2); + (*out_tets)[i*4+3] = t(i,3); + } +} + int admmpd_mesh_needs_update(ADMMPDInterfaceData *iface, Object *ob) { if (!iface) { return 0; } @@ -244,6 +278,7 @@ int admmpd_mesh_needs_update(ADMMPDInterfaceData *iface, Object *ob) // Mode or topology change? int mode = ob->soft->admmpd_mesh_mode; int mesh_type = iface->idata->mesh->type(); + if (mode != mesh_type) { return 1; } if (!iface->idata->mesh->rest_facet_verts()) { return 1; } int nx = iface->idata->mesh->rest_facet_verts()->rows(); @@ -258,9 +293,15 @@ int admmpd_update_mesh(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos if (!ob) { return 0; } if (!ob->soft) { return 0; } - if (!iface->idata) + if (!iface->idata) { iface->idata = (ADMMPDInternalData*)MEM_callocN(sizeof(ADMMPDInternalData), "ADMMPD_idata"); + } + if (!iface->idata->options) { + iface->idata->options = std::make_shared<admmpd::Options>(); + } + + options_from_object(iface,NULL,ob,iface->idata->options.get(),false); int mode = ob->soft->admmpd_mesh_mode; iface->idata->mesh.reset(); @@ -362,9 +403,13 @@ int admmpd_update_solver(ADMMPDInterfaceData *iface, Scene *sc, Object *ob, flo if (!iface->idata->mesh) { return 0; } // Reset options and data - iface->idata->options = std::make_shared<admmpd::Options>(); + if (!iface->idata->options) { + iface->idata->options = std::make_shared<admmpd::Options>(); + } iface->idata->data = std::make_shared<admmpd::SolverData>(); - iface->idata->obs.reset(); + iface->idata->obs_x0.clear(); + iface->idata->obs_x1.clear(); + iface->idata->obs_F.clear(); admmpd::Options *op = iface->idata->options.get(); options_from_object(iface,sc,ob,op,false); @@ -417,11 +462,18 @@ void admmpd_copy_to_object(ADMMPDInterfaceData *iface, Object *ob, float (*verte if (ob && ob->soft) { SoftBody *sb = ob->soft; + if (!sb->bpoint) { if (!ob->soft->bpoint) { sb->bpoint = (BodyPoint*)MEM_callocN(nx*sizeof(BodyPoint), "ADMMPD_bpoint"); } + sb->totpoint = nx; + sb->totspring = 0; + } + if (sb->totpoint != nx && sb->totpoint>0) { + MEM_freeN(sb->bpoint); + sb->bpoint = (BodyPoint*)MEM_callocN(nx*sizeof(BodyPoint), "ADMMPD_bpoint"); sb->totpoint = nx; sb->totspring = 0; } @@ -454,66 +506,6 @@ void admmpd_copy_to_object(ADMMPDInterfaceData *iface, Object *ob, float (*verte } } -void admmpd_update_obstacles( - ADMMPDInterfaceData *iface, - float *in_verts_0, - float *in_verts_1, - int nv, - unsigned int *in_faces, - int nf) -{ - if (iface==NULL || in_verts_0==NULL || in_verts_1==NULL || in_faces==NULL) { - return; - } - if (!iface->idata) { return; } - - if (nf==0 || nv==0) { return; } - int nv3 = nv*3; - int nf3 = nf*3; - iface->idata->obs.needs_sdf_recompute = false; - - if (iface->idata->obs.x0.size()!=nv3) { - iface->idata->obs.x0.resize(nv3); - iface->idata->obs.needs_sdf_recompute = true; - } - - if (iface->idata->obs.x1.size()!=nv3) { - iface->idata->obs.x1.resize(nv3); - iface->idata->obs.needs_sdf_recompute = true; - } - - if (iface->idata->obs.F.size()!=nf3) { - iface->idata->obs.F.resize(nf3); - iface->idata->obs.needs_sdf_recompute = true; - } - - for (int i=0; i<nv3; ++i) { - - // Change in x? - if (!iface->idata->obs.needs_sdf_recompute) { - if (std::abs(iface->idata->obs.x0[i]-in_verts_0[i])>1e-8 || - std::abs(iface->idata->obs.x1[i]-in_verts_1[i])>1e-8 ) { - iface->idata->obs.needs_sdf_recompute = true; - } - } - - iface->idata->obs.x0[i] = in_verts_0[i]; - iface->idata->obs.x1[i] = in_verts_1[i]; - } - for (int i=0; i<nf3; ++i) { - - // Change in f? - if (!iface->idata->obs.needs_sdf_recompute) { - if (iface->idata->obs.F[i] != in_faces[i]) { - iface->idata->obs.needs_sdf_recompute = true; - } - } - - iface->idata->obs.F[i] = in_faces[i]; - } - -} - static inline void admmpd_update_goals(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) { if (!iface) { return; } @@ -603,11 +595,18 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) return 0; } - if (!iface->idata || !iface->idata->options || !iface->idata->data) { + if (!iface->idata || !iface->idata->options || + !iface->idata->data || !iface->idata->mesh) { strcpy_error(iface, "NULL internal data"); return 0; } + std::string meshname(ob->id.name); + + // Set to true if certain conditions should + // throw a warning flag. + bool return_warning = false; + // Change only options that do not cause a reset of the solver. bool skip_solver_reset = true; options_from_object( @@ -617,6 +616,19 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) iface->idata->options.get(), skip_solver_reset); + // Disable self collision flag if the mesh does not support it. + if (iface->idata->options->self_collision && + !iface->idata->mesh->self_collision_allowed()) { + // Special message if embedded, in which the mesh is not closed. + std::string err = "Cannot do self collisions on object "+meshname+" for selected mesh type"; + if (iface->idata->mesh->type() == MESHTYPE_EMBEDDED) { + err = "Cannot do self collisions on object "+meshname+", mesh is not closed."; + } + strcpy_error(iface, err.c_str()); + iface->idata->options->self_collision = false; + return_warning = true; + } + // Goals and self collision group can change // between time steps. If the goal indices/weights change, // it will trigger a refactorization in the solver. @@ -625,12 +637,11 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) // Obstacle collisions not yet implemented // for cloth or tet mesh. - bool had_set_obstacle_error = false; if ((ob->soft->admmpd_mesh_mode == MESHTYPE_TET || ob->soft->admmpd_mesh_mode == MESHTYPE_TRIANGLE) && - iface->idata->obs.x0.size()>0) + iface->idata->obs_x0.size()>0) { - had_set_obstacle_error = true; + return_warning = true; strcpy_error(iface, "Obstacle collision not yet available for selected mesh mode."); } @@ -640,47 +651,40 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) // b) the obstacle positions have changed from the last frame bool has_obstacles = iface->idata->collision && - iface->idata->obs.x0.size() > 0 && - iface->idata->obs.F.size() > 0 && - iface->idata->obs.x0.size()==iface->idata->obs.x1.size(); - bool lerp_obstacles = - has_obstacles && - iface->idata->options->substeps>1 && - (iface->idata->obs.x0-iface->idata->obs.x1).lpNorm<Eigen::Infinity>()>1e-6; - - if (has_obstacles && iface->idata->obs.needs_sdf_recompute && !lerp_obstacles) { + iface->idata->obs_x0.size() > 0 && + iface->idata->obs_x1.size() > 0 && + iface->idata->obs_x0[0].size()==iface->idata->obs_x1[0].size(); + + int substeps = std::max(1,iface->idata->options->substeps); + int n_obs = iface->idata->obs_x0.size(); + if (has_obstacles && substeps == 1) { // no lerp necessary std::string set_obs_error = ""; if (!iface->idata->collision->set_obstacles( - iface->idata->obs.x0.data(), - iface->idata->obs.x1.data(), - iface->idata->obs.x0.size()/3, - iface->idata->obs.F.data(), - iface->idata->obs.F.size()/3, + iface->idata->obs_x0, iface->idata->obs_x1, iface->idata->obs_F, &set_obs_error)) { strcpy_error(iface, set_obs_error.c_str()); - had_set_obstacle_error = true; + return_warning = true; } } try { - Eigen::VectorXf obs_x1; // used if substeps > 1 - int substeps = std::max(1,iface->idata->options->substeps); + std::vector<Eigen::MatrixXd> obs_x1_t; for (int i=0; i<substeps; ++i) { - if (lerp_obstacles) { + // Interpolate obstacles + if (has_obstacles && substeps>1) { float t = float(i)/float(substeps-1); - obs_x1 = (1.f-t)*iface->idata->obs.x0 + t*iface->idata->obs.x1; + obs_x1_t.resize(n_obs); + for (int j=0; j<n_obs; ++j) { + obs_x1_t[j] = (1.f-t)*iface->idata->obs_x0[j] + t*iface->idata->obs_x1[j]; + } std::string set_obs_error = ""; - if (iface->idata->collision->set_obstacles( - iface->idata->obs.x0.data(), - obs_x1.data(), - iface->idata->obs.x0.size()/3, - iface->idata->obs.F.data(), - iface->idata->obs.F.size()/3, + if (!iface->idata->collision->set_obstacles( + iface->idata->obs_x0, iface->idata->obs_x1, iface->idata->obs_F, &set_obs_error)) { strcpy_error(iface, set_obs_error.c_str()); - had_set_obstacle_error = true; + return_warning = true; } } @@ -699,8 +703,7 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) return 0; } - if (had_set_obstacle_error) { - // Return warning (-1). + if (return_warning) { // We've already copied the error message. return -1; } @@ -708,6 +711,59 @@ int admmpd_solve(ADMMPDInterfaceData *iface, Object *ob, float (*vertexCos)[3]) return 1; } +void admmpd_update_obstacles(ADMMPDInterfaceData *iface, Object **obstacles, int numobjects) +{ + // Because substepping may occur, we'll buffer the start and end states + // of the obstacles. They will not be copied over to the collision pointer + // until solve(), depending on the number of substeps, in which case + // they are LERP'd + iface->idata->obs_x0.clear(); + iface->idata->obs_x1.clear(); + iface->idata->obs_F.clear(); + if (!iface) { return; } + if (!iface->idata) { return; } + if (!obstacles || numobjects==0) { return; } + + for (int i = 0; i < numobjects; ++i) { + Object *ob = obstacles[i]; + if (!ob) { + continue; // uh? + } + if (ob->type != OB_MESH) { + continue; // is not a mesh type + } + if (!ob->pd || !ob->pd->deflect) { + continue; // is a non-collider + } + if (strcmp(ob->id.name,iface->name)==0) { + continue; // skip self + } + + CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( + ob, eModifierType_Collision); + if (!cmd) { + continue; + } + + int idx = iface->idata->obs_x0.size(); + iface->idata->obs_x0.emplace_back(Eigen::MatrixXd(cmd->mvert_num,3)); + iface->idata->obs_x1.emplace_back(Eigen::MatrixXd(cmd->mvert_num,3)); + iface->idata->obs_F.emplace_back(Eigen::MatrixXi(cmd->tri_num,3)); + + for (int j=0; j<cmd->mvert_num; ++j) { + for (int k=0; k<3; ++k) { + iface->idata->obs_x0[idx](j,k) = cmd->x[j].co[k]; + iface->idata->obs_x1[idx](j,k) = cmd->xnew[j].co[k]; + } + } + + for (int j=0; j<cmd->tri_num; ++j) { + for (int k=0; k<3; ++k) { + iface->idata->obs_F[idx](j,k) = cmd->tri[j].tri[k]; + } + } + } +} #ifdef WITH_TETGEN @@ -864,6 +920,7 @@ static inline int admmpd_init_with_tetgen(ADMMPDInterfaceData *iface, Object *ob iface->idata->mesh = std::make_shared<admmpd::TetMesh>(); bool success = iface->idata->mesh->create( + iface->idata->options.get(), verts.data(), nv, faces.data(), diff --git a/intern/softbody/admmpd_api.h b/intern/softbody/admmpd_api.h index 18b0b8e3389..1055a5c9e7f 100644 --- a/intern/softbody/admmpd_api.h +++ b/intern/softbody/admmpd_api.h @@ -32,51 +32,72 @@ extern "C" { #include "DNA_scene_types.h" typedef struct ADMMPDInterfaceData { - char last_error[256]; // last error message - struct ADMMPDInternalData *idata; // internal data + /* So that ADMMPD data can be stored in a linked list. */ + struct ADMMPDInterfaceData *next, *prev; + /* The name of the object that uses this data. */ + char name[MAX_ID_NAME]; + /* If API returns 0, error stored here. */ + char last_error[256]; + /* internal data is NULL until update_mesh or update_solver. */ + struct ADMMPDInternalData *idata; } ADMMPDInterfaceData; -// Frees ADMMPDInternalData +/* Frees ADMMPDInternalData */ void admmpd_dealloc(ADMMPDInterfaceData*); -// Test if the mesh topology has changed in a way that requires re-initialization. -// Returns 0 (no update needed) or 1 (needs update) +/* Standalone function to compute embedding lattice +* but without the embedding info (for visual debugging) */ +void admmpd_compute_lattice( + int subdiv, + float *in_verts, int in_nv, + unsigned int *in_faces, int in_nf, + float **out_verts, int *out_nv, + unsigned int **out_tets, int *out_nt); + +/* Test if the mesh topology has changed in a way that requires re-initialization. +* Returns 0 (no update needed) or 1 (needs update) */ int admmpd_mesh_needs_update(ADMMPDInterfaceData*, Object*); -// Initialize the mesh. -// The SoftBody object's (ob->soft) bpoint array is also updated. -// Returns 1 on success, 0 on failure, -1 on warning +/* Initialize the mesh. +* The SoftBody object's (ob->soft) bpoint array is also updated. +* Returns 1 on success, 0 on failure, -1 on warning */ int admmpd_update_mesh(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]); -// Test if certain parameter changes require re-initialization. -// Returns 0 (no update needed) or 1 (needs update) +/* Test if certain parameter changes require re-initialization. +* Returns 0 (no update needed) or 1 (needs update) */ int admmpd_solver_needs_update(ADMMPDInterfaceData*, Scene*, Object*); -// Initialize solver variables. -// Returns 1 on success, 0 on failure, -1 on warning +/* Initialize solver variables. +* Returns 1 on success, 0 on failure, -1 on warning */ int admmpd_update_solver(ADMMPDInterfaceData*, Scene*, Object*, float (*vertexCos)[3]); -// Copies BodyPoint data (from SoftBody) -// to internal vertex position and velocity +/* Copies BodyPoint data (from SoftBody) +* to internal vertex position and velocity */ void admmpd_copy_from_object(ADMMPDInterfaceData*, Object*); -// Copies ADMM-PD data to SoftBody::bpoint and vertexCos. -// If vertexCos is NULL, it is ignored. +/* Copies ADMM-PD data to SoftBody::bpoint and vertexCos. +* If vertexCos is NULL, it is ignored. */ void admmpd_copy_to_object(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]); -// Sets the obstacle data for collisions. -// Update obstacles has a different interface because of the -// complexity of grabbing obstacle mesh data. We'll leave that in softbody.c +/* Sets the obstacle data for collisions. */ void admmpd_update_obstacles( ADMMPDInterfaceData*, - float *in_verts_0, - float *in_verts_1, - int nv, - unsigned int *in_faces, - int nf); - -// Performs a time step. Object and vertexCos are not changed. -// Returns 1 on success, 0 on failure, -1 on warning + Object**, + int numobjects); + +/* Sets the obstacle data for collisions. +* Update obstacles has a different interface because of the +* complexity of grabbing obstacle mesh data. We'll leave that in softbody.c */ +//void admmpd_update_obstacles( +// ADMMPDInterfaceData*, +// float *in_verts_0, +// float *in_verts_1, +// int nv, +// unsigned int *in_faces, +// int nf); + +/* Performs a time step. Object and vertexCos are not changed. +* Returns 1 on success, 0 on failure, -1 on warning */ int admmpd_solve(ADMMPDInterfaceData*, Object*, float (*vertexCos)[3]); #ifdef __cplusplus diff --git a/release/datafiles/locale b/release/datafiles/locale -Subproject 260b439d0fb15e3cd1efe5c120cf24f91d13d85 +Subproject 2a85baf7318e2d8a26a25e6eb8211a2395d44a7 diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 4d48bb8eaac..7604a06f7df 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -238,11 +238,11 @@ const bTheme U_theme_default = { .menu_shadow_fac = 0.3f, .menu_shadow_width = 4, .editor_outline = RGBA(0x1f1f1fff), - .icon_alpha = 1.0f, - .icon_saturation = 0.5f, .transparent_checker_primary = RGBA(0x333333ff), .transparent_checker_secondary = RGBA(0x262626ff), .transparent_checker_size = 8, + .icon_alpha = 1.0f, + .icon_saturation = 0.5f, .widget_text_cursor = RGBA(0x3399e6ff), .xaxis = RGBA(0xff3352ff), .yaxis = RGBA(0x8bdc00ff), @@ -250,9 +250,9 @@ const bTheme U_theme_default = { .gizmo_hi = RGBA(0xffffffff), .gizmo_primary = RGBA(0xf5f14dff), .gizmo_secondary = RGBA(0x63ffffff), + .gizmo_view_align = RGBA(0xffffffff), .gizmo_a = RGBA(0x4da84dff), .gizmo_b = RGBA(0xa33535ff), - .gizmo_view_align = RGBA(0xffffffff), .icon_scene = RGBA(0xe6e6e6ff), .icon_collection = RGBA(0xf4f4f4ff), .icon_object = RGBA(0xee9e5dff), @@ -379,7 +379,7 @@ const bTheme U_theme_default = { .paint_curve_handle = RGBA(0x7fff7f7f), }, .space_file = { - .back = RGBA(0x33333300), + .back = RGBA(0x28282800), .title = RGBA(0xffffffff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), @@ -404,6 +404,7 @@ const bTheme U_theme_default = { .vertex_size = 3, .outline_width = 1, .facedot_size = 4, + .row_alternate = RGBA(0xffffff07), }, .space_graph = { .back = RGBA(0x42424200), @@ -710,7 +711,6 @@ const bTheme U_theme_default = { .preview_stitch_unstitchable = RGBA(0xff0000ff), .preview_stitch_active = RGBA(0xe1d2c323), .uv_shadow = RGBA(0x707070ff), - .uv_others = RGBA(0x606060ff), .paint_curve_pivot = RGBA(0xff7f7f7f), .paint_curve_handle = RGBA(0x7fff7f7f), .metadatatext = RGBA(0xffffffff), diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 6ba9c816f3d8b342633cce3f874876f19b19118 +Subproject 88685da2c5d21cbde0412dba98f65b05f0d0bba diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 673b33a1e93..f8562241ef9 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -42,6 +42,8 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_potential_min_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-energy"), ("bpy.types.fluiddomainsettings.sndparticle_sampling_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-trappedair"), ("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"), + ("bpy.types.toolsettings.use_transform_correct_face_attributes*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-face-attributes"), + ("bpy.types.toolsettings.use_transform_correct_keep_connected*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-keep-connected"), ("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"), ("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_buoyancy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-buoyancy"), @@ -80,6 +82,7 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.sndparticle_boundary*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-boundary"), ("bpy.types.fluiddomainsettings.sndparticle_life_max*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-life-max"), ("bpy.types.fluiddomainsettings.sndparticle_life_min*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-life-min"), + ("bpy.types.fluiddomainsettings.sys_particle_maximum*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-sys-particle-maximum"), ("bpy.types.fluiddomainsettings.use_bubble_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-bubble-particles"), ("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"), ("bpy.types.materialgpencilstyle.use_overlap_strokes*", "grease_pencil/materials/grease_pencil_shader.html#bpy-types-materialgpencilstyle-use-overlap-strokes"), @@ -97,6 +100,8 @@ url_manual_mapping = ( ("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"), ("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"), ("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"), + ("bpy.ops.view3d.edit_mesh_extrude_individual_move*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-individual-move"), + ("bpy.ops.view3d.edit_mesh_extrude_manifold_normal*", "modeling/meshes/tools/extrude_manifold.html#bpy-ops-view3d-edit-mesh-extrude-manifold-normal"), ("bpy.types.cyclesrendersettings.use_distance_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-distance-cull"), ("bpy.types.fluiddomainsettings.cache_frame_offset*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-offset"), ("bpy.types.fluiddomainsettings.delete_in_obstacle*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-delete-in-obstacle"), @@ -205,6 +210,7 @@ url_manual_mapping = ( ("bpy.types.spaceview3d.use_local_collections*", "editors/3dview/properties/sidebar.html#bpy-types-spaceview3d-use-local-collections"), ("bpy.ops.object.constraint_add_with_targets*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraint-add-with-targets"), ("bpy.ops.object.vertex_group_copy_to_linked*", "modeling/meshes/properties/vertex_groups/vertex_groups.html#bpy-ops-object-vertex-group-copy-to-linked"), + ("bpy.types.curve.bevel_factor_mapping_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-start"), ("bpy.types.cyclesobjectsettings.dicing_rate*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-dicing-rate"), ("bpy.types.fluiddomainsettings.adapt_margin*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-margin"), ("bpy.types.fluiddomainsettings.burning_rate*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-burning-rate"), @@ -226,6 +232,7 @@ url_manual_mapping = ( ("bpy.ops.object.vertex_group_normalize_all*", "sculpt_paint/weight_paint/editing.html#bpy-ops-object-vertex-group-normalize-all"), ("bpy.ops.sculpt.face_set_change_visibility*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-set-change-visibility"), ("bpy.ops.sculpt.face_sets_randomize_colors*", "sculpt_paint/sculpting/editing.html#bpy-ops-sculpt-face-sets-randomize-colors"), + ("bpy.types.brush.disconnected_distance_max*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-disconnected-distance-max"), ("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"), ("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tool_settings/brushes/draw_brush.html#bpy-types-brushgpencilsettings-pen-jitter"), ("bpy.types.fluiddomainsettings.domain_type*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-domain-type"), @@ -257,6 +264,7 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.uv_random*", "grease_pencil/modes/draw/tool_settings/brushes/draw_brush.html#bpy-types-brushgpencilsettings-uv-random"), ("bpy.types.clothsettings.internal_tension*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-tension"), ("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"), + ("bpy.types.curve.bevel_factor_mapping_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-end"), ("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"), ("bpy.types.fluiddomainsettings.coba_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-coba-field"), ("bpy.types.fluiddomainsettings.flip_ratio*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flip-ratio"), @@ -380,7 +388,6 @@ url_manual_mapping = ( ("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"), ("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"), ("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"), - ("bpy.types.toolsettings.use_uv_sculpt*", "modeling/meshes/uv/uv_sculpt.html#bpy-types-toolsettings-use-uv-sculpt"), ("bpy.ops.gpencil.interpolate_reverse*", "grease_pencil/animation/interpolation.html#bpy-ops-gpencil-interpolate-reverse"), ("bpy.ops.gpencil.select_vertex_color*", "grease_pencil/selecting.html#bpy-ops-gpencil-select-vertex-color"), ("bpy.ops.gpencil.set_active_material*", "grease_pencil/modes/edit/stroke_menu.html#bpy-ops-gpencil-set-active-material"), @@ -402,6 +409,7 @@ url_manual_mapping = ( ("bpy.types.compositornodedilateerode*", "compositing/types/filter/dilate_erode.html#bpy-types-compositornodedilateerode"), ("bpy.types.compositornodeellipsemask*", "compositing/types/matte/ellipse_mask.html#bpy-types-compositornodeellipsemask"), ("bpy.types.compositornodesplitviewer*", "compositing/types/output/split_viewer.html#bpy-types-compositornodesplitviewer"), + ("bpy.types.curve.render_resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-render-resolution-u"), ("bpy.types.dynamicpaintbrushsettings*", "physics/dynamic_paint/brush.html#bpy-types-dynamicpaintbrushsettings"), ("bpy.types.fluiddomainsettings.alpha*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-alpha"), ("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"), @@ -446,6 +454,7 @@ url_manual_mapping = ( ("bpy.ops.wm.previews_batch_generate*", "files/blend/previews.html#bpy-ops-wm-previews-batch-generate"), ("bpy.types.brush.auto_smooth_factor*", "sculpt_paint/sculpting/tool_settings/brush_settings.html#bpy-types-brush-auto-smooth-factor"), ("bpy.types.brush.smooth_deform_type*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-smooth-deform-type"), + ("bpy.types.brush.use_connected_only*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-connected-only"), ("bpy.types.brush.use_cursor_overlay*", "sculpt_paint/brush/cursor.html#bpy-types-brush-use-cursor-overlay"), ("bpy.types.camera.show_passepartout*", "render/cameras.html#bpy-types-camera-show-passepartout"), ("bpy.types.compositornodebokehimage*", "compositing/types/input/bokeh_image.html#bpy-types-compositornodebokehimage"), @@ -456,6 +465,7 @@ url_manual_mapping = ( ("bpy.types.compositornodeswitchview*", "compositing/types/converter/switch_view.html#bpy-types-compositornodeswitchview"), ("bpy.types.copytransformsconstraint*", "animation/constraints/transform/copy_transforms.html#bpy-types-copytransformsconstraint"), ("bpy.types.correctivesmoothmodifier*", "modeling/modifiers/deform/corrective_smooth.html#bpy-types-correctivesmoothmodifier"), + ("bpy.types.curve.bevel_factor_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-start"), ("bpy.types.cyclesvisibilitysettings*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesvisibilitysettings"), ("bpy.types.fluiddomainsettings.beta*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-beta"), ("bpy.types.fluidmodifier.fluid_type*", "physics/fluid/type/index.html#bpy-types-fluidmodifier-fluid-type"), @@ -470,6 +480,7 @@ url_manual_mapping = ( ("bpy.types.shadernodebsdfrefraction*", "render/shader_nodes/shader/refraction.html#bpy-types-shadernodebsdfrefraction"), ("bpy.types.shadernodeoutputmaterial*", "render/shader_nodes/output/material.html#bpy-types-shadernodeoutputmaterial"), ("bpy.types.shadernodetexenvironment*", "render/shader_nodes/textures/environment.html#bpy-types-shadernodetexenvironment"), + ("bpy.types.spaceuveditor.uv_opacity*", "editors/uv/display_panel.html#bpy-types-spaceuveditor-uv-opacity"), ("bpy.types.subdividegpencilmodifier*", "grease_pencil/modifiers/generate/subdivide.html#bpy-types-subdividegpencilmodifier"), ("bpy.types.thicknessgpencilmodifier*", "grease_pencil/modifiers/deform/thickness.html#bpy-types-thicknessgpencilmodifier"), ("bpy.types.transformcacheconstraint*", "animation/constraints/transform/transform_cache.html#bpy-types-transformcacheconstraint"), @@ -501,6 +512,7 @@ url_manual_mapping = ( ("bpy.types.compositornodetransform*", "compositing/types/distort/transform.html#bpy-types-compositornodetransform"), ("bpy.types.compositornodetranslate*", "compositing/types/distort/translate.html#bpy-types-compositornodetranslate"), ("bpy.types.constraint.target_space*", "animation/constraints/interface/common.html#bpy-types-constraint-target-space"), + ("bpy.types.curve.use_deform_bounds*", "modeling/curves/properties/shape.html#bpy-types-curve-use-deform-bounds"), ("bpy.types.freestylemodulesettings*", "render/freestyle/python.html#bpy-types-freestylemodulesettings"), ("bpy.types.gpencillayer.blend_mode*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-blend-mode"), ("bpy.types.gpencillayer.mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-mask-layer"), @@ -519,6 +531,7 @@ url_manual_mapping = ( ("bpy.types.shadernodevolumescatter*", "render/shader_nodes/shader/volume_scatter.html#bpy-types-shadernodevolumescatter"), ("bpy.types.simplifygpencilmodifier*", "grease_pencil/modifiers/generate/simplify.html#bpy-types-simplifygpencilmodifier"), ("bpy.types.spacegrapheditor.cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-cursor"), + ("bpy.types.toolsettings.annotation*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation"), ("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"), ("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer.html#bpy-types-viewlayer-use-freestyle"), ("bpy.ops.gpencil.frame_clean_fill*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-frame-clean-fill"), @@ -542,6 +555,7 @@ url_manual_mapping = ( ("bpy.ops.uv.average_islands_scale*", "modeling/meshes/uv/editing.html#bpy-ops-uv-average-islands-scale"), ("bpy.types.brightcontrastmodifier*", "video_editing/sequencer/properties/modifiers.html#bpy-types-brightcontrastmodifier"), ("bpy.types.brush.cursor_color_add*", "sculpt_paint/brush/cursor.html#bpy-types-brush-cursor-color-add"), + ("bpy.types.brush.pose_deform_type*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-deform-type"), ("bpy.types.brush.pose_ik_segments*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-ik-segments"), ("bpy.types.brush.pose_origin_type*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-pose-origin-type"), ("bpy.types.camerasolverconstraint*", "animation/constraints/motion_tracking/camera_solver.html#bpy-types-camerasolverconstraint"), @@ -560,6 +574,8 @@ url_manual_mapping = ( ("bpy.types.constraint.owner_space*", "animation/constraints/interface/common.html#bpy-types-constraint-owner-space"), ("bpy.types.copylocationconstraint*", "animation/constraints/transform/copy_location.html#bpy-types-copylocationconstraint"), ("bpy.types.copyrotationconstraint*", "animation/constraints/transform/copy_rotation.html#bpy-types-copyrotationconstraint"), + ("bpy.types.curve.bevel_factor_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-end"), + ("bpy.types.curve.bevel_resolution*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-resolution"), ("bpy.types.cyclesmaterialsettings*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings"), ("bpy.types.dopesheet.show_summary*", "editors/dope_sheet/introduction.html#bpy-types-dopesheet-show-summary"), ("bpy.types.imagepaint.use_occlude*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-occlude"), @@ -576,6 +592,7 @@ url_manual_mapping = ( ("bpy.types.shadernodeparticleinfo*", "render/shader_nodes/input/particle_info.html#bpy-types-shadernodeparticleinfo"), ("bpy.types.shadernodevectorrotate*", "render/shader_nodes/vector/vector_rotate.html#bpy-types-shadernodevectorrotate"), ("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"), + ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/color/texture_mapping.html#bpy-types-texturegpencilmodifier"), ("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"), ("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"), ("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"), @@ -618,6 +635,8 @@ url_manual_mapping = ( ("bpy.types.compositornodetexture*", "compositing/types/input/texture.html#bpy-types-compositornodetexture"), ("bpy.types.compositornodetonemap*", "compositing/types/color/tone_map.html#bpy-types-compositornodetonemap"), ("bpy.types.compositornodevecblur*", "compositing/types/filter/vector_blur.html#bpy-types-compositornodevecblur"), + ("bpy.types.curve.use_fill_deform*", "modeling/curves/properties/shape.html#bpy-types-curve-use-fill-deform"), + ("bpy.types.curve.use_path_follow*", "modeling/curves/properties/path_animation.html#bpy-types-curve-use-path-follow"), ("bpy.types.dampedtrackconstraint*", "animation/constraints/tracking/damped_track.html#bpy-types-dampedtrackconstraint"), ("bpy.types.distortednoisetexture*", "render/materials/legacy_textures/types/distorted_noise.html#bpy-types-distortednoisetexture"), ("bpy.types.fluideffectorsettings*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings"), @@ -725,6 +744,7 @@ url_manual_mapping = ( ("bpy.ops.object.select_by_type*", "scene_layout/object/selecting.html#bpy-ops-object-select-by-type"), ("bpy.ops.object.select_grouped*", "scene_layout/object/selecting.html#bpy-ops-object-select-grouped"), ("bpy.ops.object.select_pattern*", "scene_layout/object/selecting.html#bpy-ops-object-select-pattern"), + ("bpy.ops.outliner.id_operation*", "editors/outliner.html#bpy-ops-outliner-id-operation"), ("bpy.ops.paint.mask_flood_fill*", "sculpt_paint/sculpting/hide_mask.html#bpy-ops-paint-mask-flood-fill"), ("bpy.ops.pose.quaternions_flip*", "animation/armatures/posing/editing/flip_quats.html#bpy-ops-pose-quaternions-flip"), ("bpy.ops.pose.transforms_clear*", "animation/armatures/posing/editing/clear.html#bpy-ops-pose-transforms-clear"), @@ -751,6 +771,9 @@ url_manual_mapping = ( ("bpy.types.compositornodescale*", "compositing/types/distort/scale.html#bpy-types-compositornodescale"), ("bpy.types.compositornodevalue*", "compositing/types/input/value.html#bpy-types-compositornodevalue"), ("bpy.types.copyscaleconstraint*", "animation/constraints/transform/copy_scale.html#bpy-types-copyscaleconstraint"), + ("bpy.types.curve.path_duration*", "modeling/curves/properties/path_animation.html#bpy-types-curve-path-duration"), + ("bpy.types.curve.use_fill_caps*", "modeling/curves/properties/geometry.html#bpy-types-curve-use-fill-caps"), + ("bpy.types.curve.use_map_taper*", "modeling/curves/properties/geometry.html#bpy-types-curve-use-map-taper"), ("bpy.types.cyclesworldsettings*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings"), ("bpy.types.fluiddomainsettings*", "physics/fluid/type/domain/index.html#bpy-types-fluiddomainsettings"), ("bpy.types.hookgpencilmodifier*", "grease_pencil/modifiers/deform/hook.html#bpy-types-hookgpencilmodifier"), @@ -828,8 +851,11 @@ url_manual_mapping = ( ("bpy.types.compositornodemask*", "compositing/types/input/mask.html#bpy-types-compositornodemask"), ("bpy.types.compositornodemath*", "compositing/types/converter/math.html#bpy-types-compositornodemath"), ("bpy.types.compositornodetime*", "compositing/types/input/time.html#bpy-types-compositornodetime"), + ("bpy.types.curve.bevel_object*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-object"), ("bpy.types.curve.resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-resolution-u"), ("bpy.types.curve.resolution_v*", "modeling/surfaces/properties/shape.html#bpy-types-curve-resolution-v"), + ("bpy.types.curve.taper_object*", "modeling/curves/properties/geometry.html#bpy-types-curve-taper-object"), + ("bpy.types.curve.twist_smooth*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-smooth"), ("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"), ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"), ("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"), @@ -912,6 +938,8 @@ url_manual_mapping = ( ("bpy.types.collisionsettings*", "physics/collision.html#bpy-types-collisionsettings"), ("bpy.types.compositornodergb*", "compositing/types/input/rgb.html#bpy-types-compositornodergb"), ("bpy.types.compositornodesep*", "editors/texture_node/types/color/combine_separate.html#bpy-types-compositornodesep"), + ("bpy.types.curve.bevel_depth*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-depth"), + ("bpy.types.curve.use_stretch*", "modeling/curves/properties/shape.html#bpy-types-curve-use-stretch"), ("bpy.types.edgesplitmodifier*", "modeling/modifiers/generate/edge_split.html#bpy-types-edgesplitmodifier"), ("bpy.types.fluidflowsettings*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings"), ("bpy.types.fmodifierenvelope*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelope"), @@ -957,6 +985,7 @@ url_manual_mapping = ( ("bpy.ops.mesh.colors_rotate*", "modeling/meshes/editing/face/face_data.html#bpy-ops-mesh-colors-rotate"), ("bpy.ops.mesh.edge_collapse*", "modeling/meshes/editing/mesh/delete.html#bpy-ops-mesh-edge-collapse"), ("bpy.ops.mesh.edge_face_add*", "modeling/meshes/editing/vertex/make_face_edge.html#bpy-ops-mesh-edge-face-add"), + ("bpy.ops.mesh.extrude_indiv*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-mesh-extrude-indiv"), ("bpy.ops.mesh.knife_project*", "modeling/meshes/editing/mesh/knife_project.html#bpy-ops-mesh-knife-project"), ("bpy.ops.mesh.loopcut_slide*", "modeling/meshes/editing/edge/loopcut_slide.html#bpy-ops-mesh-loopcut-slide"), ("bpy.ops.mesh.merge_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-merge-normals"), @@ -979,10 +1008,14 @@ url_manual_mapping = ( ("bpy.ops.transform.tosphere*", "modeling/meshes/editing/mesh/transform/to_sphere.html#bpy-ops-transform-tosphere"), ("bpy.ops.view3d.clip_border*", "editors/3dview/navigate/regions.html#bpy-ops-view3d-clip-border"), ("bpy.ops.wm.previews_ensure*", "files/blend/previews.html#bpy-ops-wm-previews-ensure"), + ("bpy.ops.wm.search_operator*", "interface/controls/templates/operator_search.html#bpy-ops-wm-search-operator"), ("bpy.types.actionconstraint*", "animation/constraints/relationship/action.html#bpy-types-actionconstraint"), ("bpy.types.addonpreferences*", "editors/preferences/addons.html#bpy-types-addonpreferences"), ("bpy.types.armaturemodifier*", "modeling/modifiers/deform/armature.html#bpy-types-armaturemodifier"), ("bpy.types.colormixsequence*", "video_editing/sequencer/strips/effects/color_mix.html#bpy-types-colormixsequence"), + ("bpy.types.curve.dimensions*", "modeling/curves/properties/shape.html#bpy-types-curve-dimensions"), + ("bpy.types.curve.twist_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-mode"), + ("bpy.types.curve.use_radius*", "modeling/curves/properties/shape.html#bpy-types-curve-use-radius"), ("bpy.types.decimatemodifier*", "modeling/modifiers/generate/decimate.html#bpy-types-decimatemodifier"), ("bpy.types.displacemodifier*", "modeling/modifiers/deform/displace.html#bpy-types-displacemodifier"), ("bpy.types.displaysafeareas*", "render/cameras.html#bpy-types-displaysafeareas"), @@ -1026,10 +1059,13 @@ url_manual_mapping = ( ("bpy.ops.fluid.bake_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-bake-guides"), ("bpy.ops.fluid.free_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-free-guides"), ("bpy.ops.font.style_toggle*", "modeling/texts/editing.html#bpy-ops-font-style-toggle"), + ("bpy.ops.gpencil.mesh_bake*", "grease_pencil/animation/tools.html#bpy-ops-gpencil-mesh-bake"), ("bpy.ops.gpencil.reproject*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-reproject"), ("bpy.ops.graph.easing_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-easing-type"), ("bpy.ops.graph.handle_type*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-handle-type"), + ("bpy.ops.mesh.bevel.vertex*", "modeling/meshes/editing/vertex/bevel_vertices.html#bpy-ops-mesh-bevel-vertex"), ("bpy.ops.mesh.delete_loose*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-delete-loose"), + ("bpy.ops.mesh.face_shading*", "modeling/meshes/editing/face/shading.html#bpy-ops-mesh-face-shading"), ("bpy.ops.mesh.flip_normals*", "modeling/meshes/editing/mesh/normals.html#bpy-ops-mesh-flip-normals"), ("bpy.ops.mesh.select_loose*", "modeling/meshes/selecting/all_by_trait.html#bpy-ops-mesh-select-loose"), ("bpy.ops.mesh.vert_connect*", "modeling/meshes/editing/vertex/connect_vertex_pairs.html#bpy-ops-mesh-vert-connect"), @@ -1053,6 +1089,8 @@ url_manual_mapping = ( ("bpy.types.booleanmodifier*", "modeling/modifiers/generate/booleans.html#bpy-types-booleanmodifier"), ("bpy.types.brush.mask_tool*", "sculpt_paint/sculpting/tools/mask.html#bpy-types-brush-mask-tool"), ("bpy.types.constraint.mute*", "animation/constraints/interface/header.html#bpy-types-constraint-mute"), + ("bpy.types.curve.eval_time*", "modeling/curves/properties/path_animation.html#bpy-types-curve-eval-time"), + ("bpy.types.curve.fill_mode*", "modeling/curves/properties/shape.html#bpy-types-curve-fill-mode"), ("bpy.types.explodemodifier*", "modeling/modifiers/physics/explode.html#bpy-types-explodemodifier"), ("bpy.types.fcurvemodifiers*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fcurvemodifiers"), ("bpy.types.floorconstraint*", "animation/constraints/relationship/floor.html#bpy-types-floorconstraint"), @@ -1148,6 +1186,7 @@ url_manual_mapping = ( ("bpy.ops.fluid.free_data*", "physics/fluid/type/domain/settings.html#bpy-ops-fluid-free-data"), ("bpy.ops.fluid.free_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-ops-fluid-free-mesh"), ("bpy.ops.font.select_all*", "modeling/texts/selecting.html#bpy-ops-font-select-all"), + ("bpy.ops.gpencil.convert*", "grease_pencil/modes/object/convert_to_geometry.html#bpy-ops-gpencil-convert"), ("bpy.ops.mesh.customdata*", "modeling/meshes/properties/custom_data.html#bpy-ops-mesh-customdata"), ("bpy.ops.mesh.edge_split*", "modeling/meshes/editing/mesh/split.html#bpy-ops-mesh-edge-split"), ("bpy.ops.mesh.fill_holes*", "modeling/meshes/editing/mesh/cleanup.html#bpy-ops-mesh-fill-holes"), @@ -1182,6 +1221,7 @@ url_manual_mapping = ( ("bpy.types.cloudstexture*", "render/materials/legacy_textures/types/clouds.html#bpy-types-cloudstexture"), ("bpy.types.colorsequence*", "video_editing/sequencer/strips/color.html#bpy-types-colorsequence"), ("bpy.types.crosssequence*", "video_editing/sequencer/strips/transitions/cross.html#bpy-types-crosssequence"), + ("bpy.types.curve.extrude*", "modeling/curves/properties/geometry.html#bpy-types-curve-extrude"), ("bpy.types.curvemodifier*", "modeling/modifiers/deform/curve.html#bpy-types-curvemodifier"), ("bpy.types.fieldsettings*", "physics/forces/force_fields/index.html#bpy-types-fieldsettings"), ("bpy.types.imagesequence*", "video_editing/sequencer/strips/movie_image.html#bpy-types-imagesequence"), @@ -1204,6 +1244,7 @@ url_manual_mapping = ( ("bpy.types.soundsequence*", "video_editing/sequencer/strips/sound.html#bpy-types-soundsequence"), ("bpy.types.spaceoutliner*", "editors/outliner.html#bpy-types-spaceoutliner"), ("bpy.types.spacetimeline*", "editors/timeline.html#bpy-types-spacetimeline"), + ("bpy.types.spaceuveditor*", "editors/uv/index.html#bpy-types-spaceuveditor"), ("bpy.types.stuccitexture*", "render/materials/legacy_textures/types/stucci.html#bpy-types-stuccitexture"), ("bpy.types.view3doverlay*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay"), ("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"), @@ -1244,6 +1285,7 @@ url_manual_mapping = ( ("bpy.types.brush.height*", "sculpt_paint/sculpting/tools/layer.html#bpy-types-brush-height"), ("bpy.types.castmodifier*", "modeling/modifiers/deform/cast.html#bpy-types-castmodifier"), ("bpy.types.colormanaged*", "render/color_management.html#bpy-types-colormanaged"), + ("bpy.types.curve.offset*", "modeling/curves/properties/geometry.html#bpy-types-curve-offset"), ("bpy.types.glowsequence*", "video_editing/sequencer/strips/effects/glow.html#bpy-types-glowsequence"), ("bpy.types.gpencillayer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer"), ("bpy.types.hookmodifier*", "modeling/modifiers/deform/hooks.html#bpy-types-hookmodifier"), @@ -1292,6 +1334,7 @@ url_manual_mapping = ( ("bpy.types.addsequence*", "video_editing/sequencer/strips/effects/add.html#bpy-types-addsequence"), ("bpy.types.camera.show*", "render/cameras.html#bpy-types-camera-show"), ("bpy.types.consoleline*", "editors/python_console.html#bpy-types-consoleline"), + ("bpy.types.curve.bevel*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel"), ("bpy.types.mesh.remesh*", "modeling/meshes/retopology.html#bpy-types-mesh-remesh"), ("bpy.types.meshstatvis*", "modeling/meshes/mesh_analysis.html#bpy-types-meshstatvis"), ("bpy.types.nodesetting*", "interface/controls/nodes/parts.html#bpy-types-nodesetting"), @@ -1346,6 +1389,7 @@ url_manual_mapping = ( ("bpy.ops.object.hook*", "modeling/meshes/editing/vertex/hooks.html#bpy-ops-object-hook"), ("bpy.ops.object.join*", "scene_layout/object/editing/join.html#bpy-ops-object-join"), ("bpy.ops.object.text*", "modeling/texts/index.html#bpy-ops-object-text"), + ("bpy.ops.uv.rip_move*", "modeling/meshes/uv/tools/rip.html#bpy-ops-uv-rip-move"), ("bpy.ops.view3d.snap*", "scene_layout/object/editing/snap.html#bpy-ops-view3d-snap"), ("bpy.types.*texspace*", "modeling/meshes/uv/uv_texture_spaces.html#bpy-types-texspace"), ("bpy.types.arealight*", "render/lights/light_object.html#bpy-types-arealight"), @@ -1414,6 +1458,7 @@ url_manual_mapping = ( ("bpy.ops.uv.select*", "editors/uv/selecting.html#bpy-ops-uv-select"), ("bpy.ops.uv.stitch*", "modeling/meshes/uv/editing.html#bpy-ops-uv-stitch"), ("bpy.ops.uv.unwrap*", "modeling/meshes/editing/uv.html#bpy-ops-uv-unwrap"), + ("bpy.ops.wm.search*", "interface/controls/templates/operator_search.html#bpy-ops-wm-search"), ("bpy.types.animviz*", "animation/motion_paths.html#bpy-types-animviz"), ("bpy.types.lattice*", "animation/lattice.html#bpy-types-lattice"), ("bpy.types.library*", "files/linked_libraries/index.html#bpy-types-library"), @@ -1479,6 +1524,7 @@ url_manual_mapping = ( ("bpy.ops.script*", "advanced/scripting/index.html#bpy-ops-script"), ("bpy.ops.sculpt*", "sculpt_paint/sculpting/index.html#bpy-ops-sculpt"), ("bpy.ops.uv.pin*", "modeling/meshes/uv/editing.html#bpy-ops-uv-pin"), + ("bpy.ops.uv.rip*", "modeling/meshes/uv/tools/rip.html#bpy-ops-uv-rip"), ("bpy.ops.view3d*", "editors/3dview/index.html#bpy-ops-view3d"), ("bpy.types.area*", "interface/window_system/areas.html#bpy-types-area"), ("bpy.types.boid*", "physics/particles/emitter/physics/boids.html#bpy-types-boid"), diff --git a/release/scripts/presets/interface_theme/blender_light.xml b/release/scripts/presets/interface_theme/blender_light.xml index 48ad0a8367e..1b48377f62c 100644 --- a/release/scripts/presets/interface_theme/blender_light.xml +++ b/release/scripts/presets/interface_theme/blender_light.xml @@ -534,13 +534,14 @@ </graph_editor> <file_browser> <ThemeFileBrowser + row_alternate="#ffffff0f" selected_file="#5680c2" > <space> <ThemeSpaceGeneric - back="#404040" + back="#999999" title="#000000" - text="#eeeeee" + text="#000000" text_hi="#ffffff" header="#adadadff" header_text="#000000" @@ -735,7 +736,6 @@ preview_stitch_unstitchable="#ff0000ff" preview_stitch_active="#e1d2c323" uv_shadow="#707070ff" - uv_others="#606060ff" frame_current="#5680c2" metadatabg="#000000" metadatatext="#ffffff" diff --git a/release/scripts/startup/bl_operators/sequencer.py b/release/scripts/startup/bl_operators/sequencer.py index a332f938afd..9b15ccf167b 100644 --- a/release/scripts/startup/bl_operators/sequencer.py +++ b/release/scripts/startup/bl_operators/sequencer.py @@ -151,7 +151,13 @@ class SequencerFadesClear(Operator): return context.scene and context.scene.sequence_editor and context.scene.sequence_editor.active_strip def execute(self, context): - fcurves = context.scene.animation_data.action.fcurves + animation_data = context.scene.animation_data + if animation_data is None: + return {'CANCELLED'} + action = animation_data.action + if action is None: + return {'CANCELLED'} + fcurves = action.fcurves fcurve_map = { curve.data_path: curve for curve in fcurves diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 83039a5c333..59abff12834 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1284,18 +1284,11 @@ class WM_OT_properties_edit(Operator): prop_type_old = type(item[prop_old]) rna_idprop_ui_prop_clear(item, prop_old) - exec_str = "del item[%r]" % prop_old - # print(exec_str) - exec(exec_str) + del item[prop_old] # Reassign - exec_str = "item[%r] = %s" % (prop, repr(value_eval)) - # print(exec_str) - exec(exec_str) - - exec_str = "item.property_overridable_library_set('[\"%s\"]', %s)" % (prop, self.is_overridable_library) - exec(exec_str) - + item[prop] = value_eval + item.property_overridable_library_set('["%s"]' % prop, self.is_overridable_library) rna_idprop_ui_prop_update(item, prop) self._last_prop[:] = [prop] @@ -1385,8 +1378,9 @@ class WM_OT_properties_edit(Operator): return {'CANCELLED'} # retrieve overridable static - exec_str = "item.is_property_overridable_library('[\"%s\"]')" % (self.property) - self.is_overridable_library = bool(eval(exec_str)) + is_overridable = item.is_property_overridable_library('["%s"]' % self.property) + self.is_overridable_library = bool(is_overridable) + # default default value prop_type, is_array = rna_idprop_value_item_type(self.get_value_eval()) diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py index 3de144c5a15..80bd8347421 100644 --- a/release/scripts/startup/bl_ui/properties_data_camera.py +++ b/release/scripts/startup/bl_ui/properties_data_camera.py @@ -385,6 +385,15 @@ class DATA_PT_camera_display(CameraButtonsPanel, Panel): col.prop(cam, "show_sensor", text="Sensor") col.prop(cam, "show_name", text="Name") + col = layout.column(align=False, heading="Passepartout") + col.use_property_decorate = False + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(cam, "show_passepartout", text="") + sub = sub.row(align=True) + sub.active = cam.show_passepartout + sub.prop(cam, "passepartout_alpha", text="") + row.prop_decorator(cam, "passepartout_alpha") class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel): bl_label = "Composition Guides" @@ -414,27 +423,6 @@ class DATA_PT_camera_display_composition_guides(CameraButtonsPanel, Panel): col.prop(cam, "show_composition_harmony_tri_b", text="Triangle B") -class DATA_PT_camera_display_passepartout(CameraButtonsPanel, Panel): - bl_label = "Passepartout" - bl_parent_id = "DATA_PT_camera_display" - bl_options = {'DEFAULT_CLOSED'} - COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'} - - def draw_header(self, context): - cam = context.camera - - self.layout.prop(cam, "show_passepartout", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - - cam = context.camera - - layout.active = cam.show_passepartout - layout.prop(cam, "passepartout_alpha", text="Opacity", slider=True) - - class DATA_PT_camera_safe_areas(CameraButtonsPanel, Panel): bl_label = "Safe Areas" bl_options = {'DEFAULT_CLOSED'} @@ -534,7 +522,6 @@ classes = ( DATA_PT_camera_background_image, DATA_PT_camera_display, DATA_PT_camera_display_composition_guides, - DATA_PT_camera_display_passepartout, DATA_PT_custom_props_camera, ) diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 9cb774e1d1b..9673fe32a9c 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -502,7 +502,7 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel): row = layout.row() mesh = context.mesh - row.prop(mesh, "remesh_mode", text="Mode", expand=True) + row.prop(mesh, "remesh_mode", text="Mode", expand=False) col = layout.column() if mesh.remesh_mode == 'VOXEL': col.prop(mesh, "remesh_voxel_size") @@ -520,8 +520,10 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel): col.operator("object.voxel_remesh", text="Voxel Remesh") elif mesh.remesh_mode == 'QUAD': col.operator("object.quadriflow_remesh", text="QuadriFlow Remesh") - else: + elif mesh.remesh_mode == 'TET': col.operator("object.tetgen_remesh", text="Tetrahedralize") + elif mesh.remesh_mode == 'TETLATTICE': + col.operator("object.tetlattice_remesh", text="Generate Lattice") diff --git a/release/scripts/startup/bl_ui/properties_physics_softbody.py b/release/scripts/startup/bl_ui/properties_physics_softbody.py index 65475b97666..4d6e93b5bf6 100644 --- a/release/scripts/startup/bl_ui/properties_physics_softbody.py +++ b/release/scripts/startup/bl_ui/properties_physics_softbody.py @@ -57,6 +57,7 @@ class PHYSICS_PT_softbody(PhysicButtonsPanel, Panel): layout.prop(softbody, "solver_mode") if softbody.solver_mode=='LEGACY': + # Moved to collision dropdown in ADMMPD layout.prop(softbody, "collision_collection") elif softbody.solver_mode=='ADMMPD': layout.prop(softbody, "admmpd_mesh_mode") @@ -350,6 +351,8 @@ class PHYSICS_PT_softbody_admmpdcollision(PhysicButtonsPanel, Panel): layout.enabled = softbody_panel_enabled(md) ob = context.object + layout.prop(softbody, "collision_collection") + layout.prop(softbody, "admmpd_ck_exp") layout.prop(softbody, "admmpd_floor_z") layout.prop(softbody, "admmpd_self_collision") diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index 1bc6d5cf71f..e336635a4ee 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1289,6 +1289,7 @@ class _defs_sculpt: layout.prop(props, "strength") row = layout.row(align=True) row.prop(props, "force_axis") + layout.prop(props, "orientation", expand=False) layout.prop(props, "cloth_mass") layout.prop(props, "cloth_damping") layout.prop(props, "use_face_sets") diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index c251b4bfcac..deac4e4c507 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -5942,7 +5942,7 @@ class VIEW3D_PT_gizmo_display(Panel): layout.separator() col = layout.column() - col.active = view.show_gizmo_context + col.active = view.show_gizmo and view.show_gizmo_context col.label(text="Object Gizmos") col.prop(scene.transform_orientation_slots[1], "type", text="") col.prop(view, "show_gizmo_object_translate", text="Move") @@ -5953,6 +5953,7 @@ class VIEW3D_PT_gizmo_display(Panel): # Match order of object type visibility col = layout.column() + col.active = view.show_gizmo col.label(text="Empty") col.prop(view, "show_gizmo_empty_image", text="Image") col.prop(view, "show_gizmo_empty_force_field", text="Force Field") @@ -6111,8 +6112,12 @@ class VIEW3D_PT_overlay_motion_tracking(Panel): bl_label = "Motion Tracking" def draw_header(self, context): + layout = self.layout view = context.space_data - self.layout.prop(view, "show_reconstruction", text=self.bl_label) + overlay = view.overlay + display_all = overlay.show_overlays + layout.active = display_all + layout.prop(view, "show_reconstruction", text=self.bl_label) def draw(self, context): layout = self.layout @@ -6134,6 +6139,7 @@ class VIEW3D_PT_overlay_motion_tracking(Panel): sub.prop(view, "show_bundle_names", text="Marker Names") col = layout.column() + col.active = display_all col.label(text="Tracks:") row = col.row(align=True) row.prop(view, "tracks_display_type", text="") @@ -6569,6 +6575,8 @@ class VIEW3D_PT_proportional_edit(Panel): layout = self.layout tool_settings = context.tool_settings col = layout.column() + col.active = (tool_settings.use_proportional_edit_objects if context.mode == 'OBJECT' + else tool_settings.use_proportional_edit) if context.mode != 'OBJECT': col.prop(tool_settings, "use_proportional_connected") diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index f62c2ead144..0cf0a99c326 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -478,7 +478,7 @@ texture_node_categories = [ def not_implemented_node(idname): NodeType = getattr(bpy.types, idname) name = NodeType.bl_rna.name - label = f"{name} (mockup)" + label = "%s (mockup)" % name return NodeItem(idname, label=label) simulation_node_categories = [ diff --git a/source/blender/blenkernel/BKE_anim_data.h b/source/blender/blenkernel/BKE_anim_data.h index 189e45f4fe5..8507793b1dc 100644 --- a/source/blender/blenkernel/BKE_anim_data.h +++ b/source/blender/blenkernel/BKE_anim_data.h @@ -35,6 +35,10 @@ struct LibraryForeachIDData; struct Main; struct ReportList; struct bAction; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; /* ************************************* */ /* AnimData API */ @@ -94,6 +98,13 @@ void BKE_animdata_merge_copy(struct Main *bmain, eAnimData_MergeCopy_Modes action_mode, bool fix_drivers); +void BKE_animdata_blend_write(struct BlendWriter *writer, struct AnimData *adt); +void BKE_animdata_blend_read_data(struct BlendDataReader *reader, struct AnimData *adt); +void BKE_animdata_blend_read_lib(struct BlendLibReader *reader, + struct ID *id, + struct AnimData *adt); +void BKE_animdata_blend_read_expand(struct BlendExpander *expander, struct AnimData *adt); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 231cd0e53c5..5ad903a0119 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 0 +#define BLENDER_FILE_SUBVERSION 1 /* 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_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 92bfa3a9490..25360d4b3fa 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -37,6 +37,8 @@ struct BMesh; struct CustomData; struct CustomData_MeshMasks; struct ID; +struct BlendWriter; +struct BlendDataReader; typedef uint64_t CustomDataMask; /*a data type large enough to hold 1 element from any customdata layer type*/ @@ -571,6 +573,14 @@ typedef struct CustomDataTransferLayerMap { void CustomData_data_transfer(const struct MeshPairRemap *me_remap, const CustomDataTransferLayerMap *laymap); +/* .blend file I/O */ +void CustomData_blend_write(struct BlendWriter *writer, + struct CustomData *data, + int count, + CustomDataMask cddata_mask, + struct ID *id); +void CustomData_blend_read(struct BlendDataReader *reader, struct CustomData *data, int count); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_deform.h b/source/blender/blenkernel/BKE_deform.h index 35111d5240e..a9f81676a7a 100644 --- a/source/blender/blenkernel/BKE_deform.h +++ b/source/blender/blenkernel/BKE_deform.h @@ -35,6 +35,8 @@ struct MLoop; struct MPoly; struct Object; struct bDeformGroup; +struct BlendWriter; +struct BlendDataReader; struct bDeformGroup *BKE_object_defgroup_new(struct Object *ob, const char *name); void BKE_defgroup_copy_list(struct ListBase *lb1, const struct ListBase *lb2); @@ -162,6 +164,11 @@ void BKE_defvert_extract_vgroup_to_polyweights(struct MDeformVert *dvert, void BKE_defvert_weight_to_rgb(float r_rgb[3], const float weight); +void BKE_defvert_blend_write(struct BlendWriter *writer, int count, struct MDeformVert *dvlist); +void BKE_defvert_blend_read(struct BlendDataReader *reader, + int count, + struct MDeformVert *mdverts); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_fcurve.h b/source/blender/blenkernel/BKE_fcurve.h index 3717eb0f282..b316fc28726 100644 --- a/source/blender/blenkernel/BKE_fcurve.h +++ b/source/blender/blenkernel/BKE_fcurve.h @@ -44,6 +44,10 @@ struct PropertyRNA; struct StructRNA; struct bAction; struct bContext; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; /* ************** Keyframe Tools ***************** */ @@ -311,6 +315,24 @@ float fcurve_samplingcb_evalcurve(struct FCurve *fcu, void *data, float evaltime void fcurve_store_samples( struct FCurve *fcu, void *data, int start, int end, FcuSampleFunc sample_cb); +/* ************* F-Curve .blend file API ******************** */ + +void BKE_fmodifiers_blend_write(struct BlendWriter *writer, struct ListBase *fmodifiers); +void BKE_fmodifiers_blend_read_data(struct BlendDataReader *reader, + ListBase *fmodifiers, + struct FCurve *curve); +void BKE_fmodifiers_blend_read_lib(struct BlendLibReader *reader, + struct ID *id, + struct ListBase *fmodifiers); +void BKE_fmodifiers_blend_read_expand(struct BlendExpander *expander, struct ListBase *fmodifiers); + +void BKE_fcurve_blend_write(struct BlendWriter *writer, struct ListBase *fcurves); +void BKE_fcurve_blend_read_data(struct BlendDataReader *reader, struct ListBase *fcurves); +void BKE_fcurve_blend_read_lib(struct BlendLibReader *reader, + struct ID *id, + struct ListBase *fcurves); +void BKE_fcurve_blend_read_expand(struct BlendExpander *expander, struct ListBase *fcurves); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 9ffd496616d..5e16c9c979c 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -71,7 +71,6 @@ typedef struct Global { * * -1: Disable faster motion paths computation (since 08/2018). * * 1 - 30: EEVEE debug/stats values (01/2018). * * 101: Enable UI debug drawing of fullscreen area's corner widget (10/2014). - * * 527: Old mysterious switch in behavior of MeshDeform modifier (before 04/2010). * * 666: Use quicker batch delete for outliners' delete hierarchy (01/2019). * * 777: Enable UI node panel's sockets polling (11/2011). * * 799: Enable some mysterious new depsgraph behavior (05/2015). diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index 58629cde6ec..e7f9ad207a1 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -30,6 +30,10 @@ extern "C" { struct ID; struct IDProperty; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; typedef union IDPropertyTemplate { int i; @@ -196,6 +200,14 @@ void IDP_repr_fn(const IDProperty *prop, void *user_data); void IDP_print(const struct IDProperty *prop); +void IDP_BlendWrite(struct BlendWriter *writer, const struct IDProperty *prop); +void IDP_BlendReadData_impl(struct BlendDataReader *reader, + IDProperty **prop, + const char *caller_func_id); +#define IDP_BlendDataRead(reader, prop) IDP_BlendReadData_impl(reader, prop, __func__) +void IDP_BlendReadLib(struct BlendLibReader *reader, IDProperty *prop); +void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index aefba9ef02a..57b005d1a1e 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -34,6 +34,10 @@ extern "C" { struct ID; struct LibraryForeachIDData; struct Main; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; /** IDTypeInfo.flags. */ enum { @@ -89,6 +93,13 @@ typedef void (*IDTypeForeachCacheFunction)(struct ID *id, IDTypeForeachCacheFunctionCallback function_callback, void *user_data); +typedef void (*IDTypeBlendWriteFunction)(struct BlendWriter *writer, + struct ID *id, + const void *id_address); +typedef void (*IDTypeBlendReadDataFunction)(struct BlendDataReader *reader, struct ID *id); +typedef void (*IDTypeBlendReadLibFunction)(struct BlendLibReader *reader, struct ID *id); +typedef void (*IDTypeBlendReadExpandFunction)(struct BlendExpander *expander, struct ID *id); + typedef struct IDTypeInfo { /* ********** General IDType data. ********** */ @@ -161,6 +172,26 @@ typedef struct IDTypeInfo { * Iterator over all cache pointers of given ID. */ IDTypeForeachCacheFunction foreach_cache; + + /** + * Write all structs that should be saved in a .blend file. + */ + IDTypeBlendWriteFunction blend_write; + + /** + * Update pointers for all structs directly owned by this data block. + */ + IDTypeBlendReadDataFunction blend_read_data; + + /** + * Update pointers to other id data blocks. + */ + IDTypeBlendReadLibFunction blend_read_lib; + + /** + * Specify which other id data blocks should be loaded when the current one is loaded. + */ + IDTypeBlendReadExpandFunction blend_read_expand; } IDTypeInfo; /* ********** Declaration of each IDTypeInfo. ********** */ diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index a2cbe537349..7768e903ac3 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -59,6 +59,8 @@ struct Main; struct PointerRNA; struct PropertyRNA; struct bContext; +struct BlendWriter; +struct BlendDataReader; size_t BKE_libblock_get_alloc_info(short type, const char **name); void *BKE_libblock_alloc_notest(short type) ATTR_WARN_UNUSED_RESULT; @@ -285,6 +287,8 @@ bool BKE_id_is_in_global_main(struct ID *id); void BKE_id_ordered_list(struct ListBase *ordered_lb, const struct ListBase *lb); void BKE_id_reorder(const struct ListBase *lb, struct ID *id, struct ID *relative, bool after); +void BKE_id_blend_write(struct BlendWriter *writer, struct ID *id); + #define IS_TAGGED(_id) ((_id) && (((ID *)_id)->tag & LIB_TAG_DOIT)) #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index 5843992b25c..9a5700d2fbd 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -68,11 +68,20 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain, struct ID *id_root, const uint tag, const bool do_create_main_relashionships); +void BKE_lib_override_library_override_group_tag(struct Main *bmain, + struct ID *id_root, + const uint tag, + const bool do_create_main_relashionships); bool BKE_lib_override_library_create(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct ID *id_root, struct ID *id_reference); +bool BKE_lib_override_library_resync(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer, + struct ID *id_root); +void BKE_lib_override_library_delete(struct Main *bmain, struct ID *id_root); struct IDOverrideLibraryProperty *BKE_lib_override_library_property_find( struct IDOverrideLibrary *override, const char *rna_path); diff --git a/source/blender/blenkernel/BKE_mesh_mapping.h b/source/blender/blenkernel/BKE_mesh_mapping.h index 140a60dbdb7..cbd992bbbec 100644 --- a/source/blender/blenkernel/BKE_mesh_mapping.h +++ b/source/blender/blenkernel/BKE_mesh_mapping.h @@ -48,9 +48,6 @@ typedef struct UvMapVert { unsigned int poly_index; unsigned short loop_of_poly_index; bool separate; - /* Zero-ed by map creation, left for use by specific areas. Is not - * initialized to anything. */ - unsigned char flag; } UvMapVert; /* UvElement stores per uv information so that we can quickly access information for a uv. diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h index 277747e3e27..4db933fdc09 100644 --- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.h +++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.h @@ -59,7 +59,10 @@ struct Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(struct Mesh *mesh, struct Mesh *BKE_mesh_remesh_tetgen_to_mesh_nomain(struct Mesh *mesh, unsigned int **tets, int *numtets); - +struct Mesh *BKE_mesh_remesh_tetlattice_to_mesh_nomain(struct Mesh *mesh, + int subdivisions, + unsigned int **tets, + int *numtets); /* Data reprojection functions */ void BKE_mesh_remesh_reproject_paint_mask(struct Mesh *target, struct Mesh *source); void BKE_remesh_reproject_vertex_paint(struct Mesh *target, struct Mesh *source); diff --git a/source/blender/blenkernel/BKE_mesh_runtime.h b/source/blender/blenkernel/BKE_mesh_runtime.h index 267be4f44fd..87b55c581a2 100644 --- a/source/blender/blenkernel/BKE_mesh_runtime.h +++ b/source/blender/blenkernel/BKE_mesh_runtime.h @@ -72,9 +72,9 @@ struct Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph, const struct CustomData_MeshMasks *dataMask); struct Mesh *mesh_create_eval_final(struct Depsgraph *depsgraph, - struct Scene *scene, - struct Object *ob, - const struct CustomData_MeshMasks *dataMask); + struct Scene *scene, + struct Object *ob, + const struct CustomData_MeshMasks *dataMask); struct Mesh *mesh_create_eval_final_index_render(struct Depsgraph *depsgraph, struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_nla.h b/source/blender/blenkernel/BKE_nla.h index 8b3231e5302..4cded6b9d6a 100644 --- a/source/blender/blenkernel/BKE_nla.h +++ b/source/blender/blenkernel/BKE_nla.h @@ -38,6 +38,10 @@ struct bAction; struct PointerRNA; struct PropertyRNA; +struct BlendWriter; +struct BlendDataReader; +struct BlendLibReader; +struct BlendExpander; /* ----------------------------- */ /* Data Management */ @@ -102,6 +106,7 @@ void BKE_nlastrip_set_active(struct AnimData *adt, struct NlaStrip *strip); bool BKE_nlastrip_within_bounds(struct NlaStrip *strip, float min, float max); void BKE_nlastrip_recalculate_bounds(struct NlaStrip *strip); +void BKE_nlastrip_recalculate_bounds_sync_action(struct NlaStrip *strip); void BKE_nlastrip_validate_name(struct AnimData *adt, struct NlaStrip *strip); @@ -145,6 +150,14 @@ enum eNlaTime_ConvertModes { float BKE_nla_tweakedit_remap(struct AnimData *adt, float cframe, short mode); +/* ----------------------------- */ +/* .blend file API */ + +void BKE_nla_blend_write(struct BlendWriter *writer, struct ListBase *tracks); +void BKE_nla_blend_read_data(struct BlendDataReader *reader, struct ListBase *tracks); +void BKE_nla_blend_read_lib(struct BlendLibReader *reader, struct ID *id, struct ListBase *tracks); +void BKE_nla_blend_read_expand(struct BlendExpander *expander, struct ListBase *tracks); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index b2794e9d9d6..dfc75e2fd54 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -259,6 +259,16 @@ typedef struct SculptPoseIKChain { /* Cloth Brush */ +typedef enum eSculptClothConstraintType { + /* Constraint that creates the structure of the cloth. */ + SCULPT_CLOTH_CONSTRAINT_STRUCTURAL = 0, + /* Constraint that references the position of a vertex and a position in deformation_pos which + can be deformed by the tools. */ + SCULPT_CLOTH_CONSTRAINT_DEFORMATION = 1, + /* Constarint that references the vertex position and its initial position. */ + SCULPT_CLOTH_CONSTRAINT_SOFTBODY = 2, +} eSculptClothConstraintType; + typedef struct SculptClothLengthConstraint { /* Elements that are affected by the constraint. */ /* Element a should always be a mesh vertex with the index stored in elem_index_a as it is always @@ -274,6 +284,8 @@ typedef struct SculptClothLengthConstraint { float length; float strength; + + eSculptClothConstraintType type; } SculptClothLengthConstraint; typedef struct SculptClothSimulation { @@ -287,6 +299,7 @@ typedef struct SculptClothSimulation { * final positions of the simulated vertices are updated with constraints that use these points * as targets. */ float (*deformation_pos)[3]; + float *deformation_strength; float mass; float damping; diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h index c2059144388..ae1e437cd60 100644 --- a/source/blender/blenkernel/BKE_rigidbody.h +++ b/source/blender/blenkernel/BKE_rigidbody.h @@ -143,6 +143,7 @@ void BKE_rigidbody_aftertrans_update(struct Object *ob, float rotAngle); void BKE_rigidbody_sync_transforms(struct RigidBodyWorld *rbw, struct Object *ob, float ctime); bool BKE_rigidbody_check_sim_running(struct RigidBodyWorld *rbw, float ctime); +bool BKE_rigidbody_is_affected_by_simulation(struct Object *ob); void BKE_rigidbody_cache_reset(struct RigidBodyWorld *rbw); void BKE_rigidbody_rebuild_world(struct Depsgraph *depsgraph, struct Scene *scene, float ctime); void BKE_rigidbody_do_simulation(struct Depsgraph *depsgraph, struct Scene *scene, float ctime); diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 3ab923f05f6..05966ec3989 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -110,8 +110,7 @@ void BKE_toolsettings_free(struct ToolSettings *toolsettings); struct Scene *BKE_scene_duplicate(struct Main *bmain, struct Scene *sce, eSceneCopyMethod type); void BKE_scene_groups_relink(struct Scene *sce); -struct Scene *BKE_scene_find_from_view_layer(const struct Main *bmain, - const struct ViewLayer *layer); +bool BKE_scene_has_view_layer(const struct Scene *scene, const struct ViewLayer *layer); struct Scene *BKE_scene_find_from_collection(const struct Main *bmain, const struct Collection *collection); @@ -220,10 +219,12 @@ void BKE_scene_ensure_depsgraph_hash(struct Scene *scene); void BKE_scene_free_depsgraph_hash(struct Scene *scene); void BKE_scene_free_view_layer_depsgraph(struct Scene *scene, struct ViewLayer *view_layer); -struct Depsgraph *BKE_scene_get_depsgraph(struct Main *bmain, - struct Scene *scene, - struct ViewLayer *view_layer, - bool allocate); +/* Do not allocate new depsgraph. */ +struct Depsgraph *BKE_scene_get_depsgraph(struct Scene *scene, struct ViewLayer *view_layer); +/* Allocate new depsgraph if necessary. */ +struct Depsgraph *BKE_scene_ensure_depsgraph(struct Main *bmain, + struct Scene *scene, + struct ViewLayer *view_layer); struct GHash *BKE_scene_undo_depsgraphs_extract(struct Main *bmain); void BKE_scene_undo_depsgraphs_restore(struct Main *bmain, struct GHash *depsgraph_extract); diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index b6d901c8ef2..fd881175872 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -62,30 +62,34 @@ typedef struct SeqIterator { int valid; } SeqIterator; -void BKE_sequence_iterator_begin(struct Editing *ed, SeqIterator *iter, bool use_pointer); +void BKE_sequence_iterator_begin(struct Editing *ed, + SeqIterator *iter, + const bool use_current_sequences); void BKE_sequence_iterator_next(SeqIterator *iter); void BKE_sequence_iterator_end(SeqIterator *iter); -#define SEQP_BEGIN(_ed, _seq) \ - { \ - SeqIterator iter_macro; \ - for (BKE_sequence_iterator_begin(_ed, &iter_macro, true); iter_macro.valid; \ - BKE_sequence_iterator_next(&iter_macro)) { \ - _seq = iter_macro.seq; - -#define SEQ_BEGIN(ed, _seq) \ +#define SEQ_ALL_BEGIN(ed, _seq) \ { \ SeqIterator iter_macro; \ for (BKE_sequence_iterator_begin(ed, &iter_macro, false); iter_macro.valid; \ BKE_sequence_iterator_next(&iter_macro)) { \ _seq = iter_macro.seq; -#define SEQ_END \ +#define SEQ_ALL_END \ } \ BKE_sequence_iterator_end(&iter_macro); \ } \ ((void)0) +#define SEQ_CURRENT_BEGIN(_ed, _seq) \ + { \ + SeqIterator iter_macro; \ + for (BKE_sequence_iterator_begin(_ed, &iter_macro, true); iter_macro.valid; \ + BKE_sequence_iterator_next(&iter_macro)) { \ + _seq = iter_macro.seq; + +#define SEQ_CURRENT_END SEQ_ALL_END + typedef enum eSeqTaskId { SEQ_TASK_MAIN_RENDER, SEQ_TASK_PREFETCH_RENDER, diff --git a/source/blender/blenkernel/BKE_softbody.h b/source/blender/blenkernel/BKE_softbody.h index fb377302c7a..f0bc521664f 100644 --- a/source/blender/blenkernel/BKE_softbody.h +++ b/source/blender/blenkernel/BKE_softbody.h @@ -50,8 +50,11 @@ typedef struct BodyPoint { /* allocates and initializes general main data */ extern struct SoftBody *sbNew(struct Scene *scene); -/* reads custom structs for file i/o */ -extern void sbCustomRead(struct Object *ob); +/* copies external solver data from src to dest */ +extern void sbExternalCopy(struct Object *dest, struct Object *src); + +/* initializes settings for external solvers */ +extern void sbExternalSetDefault(struct SoftBody *sb); /* frees internal data and softbody itself */ extern void sbFree(struct Object *ob); diff --git a/source/blender/blenkernel/intern/DerivedMesh.c b/source/blender/blenkernel/intern/DerivedMesh.c index 4f587abd9f0..263f63cb6da 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.c +++ b/source/blender/blenkernel/intern/DerivedMesh.c @@ -1991,9 +1991,9 @@ Mesh *mesh_get_eval_deform(struct Depsgraph *depsgraph, } Mesh *mesh_create_eval_final(Depsgraph *depsgraph, - Scene *scene, - Object *ob, - const CustomData_MeshMasks *dataMask) + Scene *scene, + Object *ob, + const CustomData_MeshMasks *dataMask) { Mesh *final; diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 85ac2c693cb..089d8bef09e 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -184,6 +184,12 @@ IDTypeInfo IDType_ID_AC = { .free_data = action_free_data, .make_local = NULL, .foreach_id = action_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* ***************** Library data level operations on action ************** */ diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 038a0d14ddb..5ce449c5000 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -54,6 +54,8 @@ #include "DEG_depsgraph.h" +#include "BLO_read_write.h" + #include "RNA_access.h" #include "CLG_log.h" @@ -1497,3 +1499,85 @@ void BKE_animdata_fix_paths_rename_all(ID *ref_id, /* scenes */ RENAMEFIX_ANIM_NODETREE_IDS(bmain->scenes.first, Scene); } + +/* .blend file API -------------------------------------------- */ + +void BKE_animdata_blend_write(BlendWriter *writer, struct AnimData *adt) +{ + /* firstly, just write the AnimData block */ + BLO_write_struct(writer, AnimData, adt); + + /* write drivers */ + BKE_fcurve_blend_write(writer, &adt->drivers); + + /* write overrides */ + // FIXME: are these needed? + LISTBASE_FOREACH (AnimOverride *, aor, &adt->overrides) { + /* overrides consist of base data + rna_path */ + BLO_write_struct(writer, AnimOverride, aor); + BLO_write_string(writer, aor->rna_path); + } + + // TODO write the remaps (if they are needed) + + /* write NLA data */ + BKE_nla_blend_write(writer, &adt->nla_tracks); +} + +void BKE_animdata_blend_read_data(BlendDataReader *reader, AnimData *adt) +{ + /* NOTE: must have called BLO_read_data_address already before doing this... */ + if (adt == NULL) { + return; + } + + /* link drivers */ + BLO_read_list(reader, &adt->drivers); + BKE_fcurve_blend_read_data(reader, &adt->drivers); + adt->driver_array = NULL; + + /* link overrides */ + // TODO... + + /* link NLA-data */ + BLO_read_list(reader, &adt->nla_tracks); + BKE_nla_blend_read_data(reader, &adt->nla_tracks); + + /* relink active track/strip - even though strictly speaking this should only be used + * if we're in 'tweaking mode', we need to be able to have this loaded back for + * undo, but also since users may not exit tweakmode before saving (#24535) + */ + // TODO: it's not really nice that anyone should be able to save the file in this + // state, but it's going to be too hard to enforce this single case... + BLO_read_data_address(reader, &adt->act_track); + BLO_read_data_address(reader, &adt->actstrip); +} + +void BKE_animdata_blend_read_lib(BlendLibReader *reader, ID *id, AnimData *adt) +{ + if (adt == NULL) { + return; + } + + /* link action data */ + BLO_read_id_address(reader, id->lib, &adt->action); + BLO_read_id_address(reader, id->lib, &adt->tmpact); + + /* link drivers */ + BKE_fcurve_blend_read_lib(reader, id, &adt->drivers); + + /* overrides don't have lib-link for now, so no need to do anything */ + + /* link NLA-data */ + BKE_nla_blend_read_lib(reader, id, &adt->nla_tracks); +} + +void BKE_animdata_blend_read_expand(struct BlendExpander *expander, AnimData *adt) +{ + /* own action */ + BLO_expand(expander, adt->action); + BLO_expand(expander, adt->tmpact); + + /* drivers - assume that these F-Curves have driver data to be in this list... */ + BKE_fcurve_blend_read_expand(expander, &adt->drivers); +} diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 5b5e32f1d81..69e70cffdb2 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -2192,7 +2192,15 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels, if (is_inplace_tweak) { /* edit active action in-place according to its active strip, so copy the data */ memcpy(dummy_strip, adt->actstrip, sizeof(NlaStrip)); + /* Prevents nla eval from considering active strip's adj strips. + * For user, this means entering tweak mode on a strip ignores evaluating adjacent strips + * in the same track. */ dummy_strip->next = dummy_strip->prev = NULL; + + /* If tweaked strip is syncing action length, then evaluate using action length. */ + if (dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH) { + BKE_nlastrip_recalculate_bounds_sync_action(dummy_strip); + } } else { /* set settings of dummy NLA strip from AnimData settings */ @@ -2237,9 +2245,11 @@ static bool animsys_evaluate_nla(NlaEvalData *echannels, /* If computing the context for keyframing, store data there instead of the list. */ else { /* The extend mode here effectively controls - * whether it is possible to key-frame beyond the ends. */ - dummy_strip->extendmode = is_inplace_tweak ? NLASTRIP_EXTEND_NOTHING : - NLASTRIP_EXTEND_HOLD; + * whether it is possible to key-frame beyond the ends.*/ + dummy_strip->extendmode = (is_inplace_tweak && + !(dummy_strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) ? + NLASTRIP_EXTEND_NOTHING : + NLASTRIP_EXTEND_HOLD; r_context->eval_strip = nes = nlastrips_ctime_get_strip( NULL, &dummy_trackslist, -1, anim_eval_context, flush_to_original); @@ -2860,7 +2870,7 @@ void BKE_animsys_eval_driver(Depsgraph *depsgraph, ID *id, int driver_index, FCu /* set error-flag if evaluation failed */ if (ok == 0) { - CLOG_ERROR(&LOG, "invalid driver - %s[%d]", fcu->rna_path, fcu->array_index); + CLOG_WARN(&LOG, "invalid driver - %s[%d]", fcu->rna_path, fcu->array_index); driver_orig->flag |= DRIVER_FLAG_INVALID; } } diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 631ce4edd20..3b73702cf0f 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -180,6 +180,12 @@ IDTypeInfo IDType_ID_AR = { .free_data = armature_free_data, .make_local = NULL, .foreach_id = armature_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 1833ad5a748..4ab8ea5a647 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -703,7 +703,7 @@ void BKE_bpath_traverse_id( if (scene->ed) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (SEQ_HAS_PATH(seq)) { StripElem *se = seq->strip->stripdata; @@ -732,7 +732,7 @@ void BKE_bpath_traverse_id( } } } - SEQ_END; + SEQ_ALL_END; } break; } diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 8cd30c2241f..0749f29879c 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -210,6 +210,12 @@ IDTypeInfo IDType_ID_BR = { .free_data = brush_free_data, .make_local = brush_make_local, .foreach_id = brush_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static RNG *brush_rng; @@ -1539,7 +1545,6 @@ void BKE_brush_sculpt_reset(Brush *br) break; case SCULPT_TOOL_SMOOTH: br->flag &= ~BRUSH_SPACE_ATTEN; - br->automasking_flags |= BRUSH_AUTOMASKING_BOUNDARY_EDGES; br->spacing = 5; br->alpha = 0.7f; br->surface_smooth_shape_preservation = 0.5f; diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 9ad6ae84c5c..f3386df03c8 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -100,6 +100,12 @@ IDTypeInfo IDType_ID_CF = { .free_data = cache_file_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* TODO: make this per cache file to avoid global locks. */ diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index d8b4150b2b1..0c0ad7a57ab 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -128,6 +128,12 @@ IDTypeInfo IDType_ID_CA = { .free_data = camera_free_data, .make_local = camera_make_local, .foreach_id = camera_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 0d65ee5faa3..8975be2b618 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -173,6 +173,12 @@ IDTypeInfo IDType_ID_GR = { .free_data = collection_free_data, .make_local = NULL, .foreach_id = collection_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \} */ @@ -408,18 +414,28 @@ static Collection *collection_duplicate_recursive(Main *bmain, } if (do_objects) { + /* We need to first duplicate the objects in a separate loop, to support the master collection + * case, where both old and new collections are the same. + * Otherwise, depending on naming scheme and sorting, we may end up duplicating the new objects + * we just added, in some infinite loop. */ + LISTBASE_FOREACH (CollectionObject *, cob, &collection_old->gobject) { + Object *ob_old = cob->ob; + + if (ob_old->id.newid == NULL) { + BKE_object_duplicate( + bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS); + } + } + /* We can loop on collection_old's objects, but have to consider it mutable because with master * collections collection_old and collection_new are the same data here. */ LISTBASE_FOREACH_MUTABLE (CollectionObject *, cob, &collection_old->gobject) { Object *ob_old = cob->ob; Object *ob_new = (Object *)ob_old->id.newid; - if (ob_new == NULL) { - ob_new = BKE_object_duplicate( - bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS); - } - - if (ob_new == ob_old) { + /* New object can be NULL in master collection case, since new and old objects are in same + * collection. */ + if (ELEM(ob_new, ob_old, NULL)) { continue; } diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 05c521e3b94..115980d577e 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -647,9 +647,9 @@ DO_INLINE void collision_interpolateOnTriangle(float to[3], VECADDMUL(to, v3, w3); } -static void cloth_selfcollision_impulse_vert(const float clamp_sq, - const float impulse[3], - struct ClothVertex *vert) +static void cloth_collision_impulse_vert(const float clamp_sq, + const float impulse[3], + struct ClothVertex *vert) { float impulse_len_sq = len_squared_v3(impulse); @@ -681,7 +681,7 @@ static int cloth_collision_response_static(ClothModifierData *clmd, { int result = 0; Cloth *cloth = clmd->clothObject; - const float clamp_sq = square_f(clmd->coll_parms->self_clamp * dt); + const float clamp_sq = square_f(clmd->coll_parms->clamp * dt); const float time_multiplier = 1.0f / (clmd->sim_parms->dt * clmd->sim_parms->timescale); const float epsilon2 = BLI_bvhtree_get_epsilon(collmd->bvhtree); const float min_distance = (clmd->coll_parms->epsilon + epsilon2) * (8.0f / 9.0f); @@ -828,10 +828,10 @@ static int cloth_collision_response_static(ClothModifierData *clmd, } if (result) { - cloth_selfcollision_impulse_vert(clamp_sq, i1, &cloth->verts[collpair->ap1]); - cloth_selfcollision_impulse_vert(clamp_sq, i2, &cloth->verts[collpair->ap2]); + cloth_collision_impulse_vert(clamp_sq, i1, &cloth->verts[collpair->ap1]); + cloth_collision_impulse_vert(clamp_sq, i2, &cloth->verts[collpair->ap2]); if (!is_hair) { - cloth_selfcollision_impulse_vert(clamp_sq, i3, &cloth->verts[collpair->ap3]); + cloth_collision_impulse_vert(clamp_sq, i3, &cloth->verts[collpair->ap3]); } } } @@ -987,13 +987,13 @@ static int cloth_selfcollision_response_static(ClothModifierData *clmd, } if (result) { - cloth_selfcollision_impulse_vert(clamp_sq, ia[0], &cloth->verts[collpair->ap1]); - cloth_selfcollision_impulse_vert(clamp_sq, ia[1], &cloth->verts[collpair->ap2]); - cloth_selfcollision_impulse_vert(clamp_sq, ia[2], &cloth->verts[collpair->ap3]); + cloth_collision_impulse_vert(clamp_sq, ia[0], &cloth->verts[collpair->ap1]); + cloth_collision_impulse_vert(clamp_sq, ia[1], &cloth->verts[collpair->ap2]); + cloth_collision_impulse_vert(clamp_sq, ia[2], &cloth->verts[collpair->ap3]); - cloth_selfcollision_impulse_vert(clamp_sq, ib[0], &cloth->verts[collpair->bp1]); - cloth_selfcollision_impulse_vert(clamp_sq, ib[1], &cloth->verts[collpair->bp2]); - cloth_selfcollision_impulse_vert(clamp_sq, ib[2], &cloth->verts[collpair->bp3]); + cloth_collision_impulse_vert(clamp_sq, ib[0], &cloth->verts[collpair->bp1]); + cloth_collision_impulse_vert(clamp_sq, ib[1], &cloth->verts[collpair->bp2]); + cloth_collision_impulse_vert(clamp_sq, ib[2], &cloth->verts[collpair->bp3]); } } diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index e9ba3a5f873..dce14c4c082 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -1349,7 +1349,7 @@ Depsgraph *CTX_data_depsgraph_pointer(const bContext *C) Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); /* Dependency graph might have been just allocated, and hence it will not be marked. * This confuses redo system due to the lack of flushing changes back to the original data. * In the future we would need to check whether the CTX_wm_window(C) is in editing mode (as an @@ -1377,8 +1377,7 @@ Depsgraph *CTX_data_ensure_evaluated_depsgraph(const bContext *C) Depsgraph *CTX_data_depsgraph_on_load(const bContext *C) { - Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - return BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + return BKE_scene_get_depsgraph(scene, view_layer); } diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index 45ca89ac47e..44d5bbfd710 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -146,6 +146,12 @@ IDTypeInfo IDType_ID_CU = { .free_data = curve_free_data, .make_local = NULL, .foreach_id = curve_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static int cu_isectLL(const float v1[3], diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index f728436a759..b75efb6cdf1 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -35,6 +35,8 @@ #include "DNA_meshdata_types.h" #include "DNA_pointcloud_types.h" +#include "BLI_bitmap.h" +#include "BLI_endian_switch.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" #include "BLI_mempool.h" @@ -47,10 +49,14 @@ #include "BKE_customdata.h" #include "BKE_customdata_file.h" +#include "BKE_deform.h" #include "BKE_main.h" #include "BKE_mesh_mapping.h" #include "BKE_mesh_remap.h" #include "BKE_multires.h" +#include "BKE_subsurf.h" + +#include "BLO_read_write.h" #include "bmesh.h" @@ -5155,3 +5161,181 @@ void CustomData_data_transfer(const MeshPairRemap *me_remap, MEM_SAFE_FREE(tmp_data_src); } + +static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external) +{ + if (mdlist) { + BLO_write_struct_array(writer, MDisps, count, mdlist); + for (int i = 0; i < count; i++) { + MDisps *md = &mdlist[i]; + if (md->disps) { + if (!external) { + BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]); + } + } + + if (md->hidden) { + BLO_write_raw(writer, BLI_BITMAP_SIZE(md->totdisp), md->hidden); + } + } + } +} + +static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask *grid_paint_mask) +{ + if (grid_paint_mask) { + BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask); + for (int i = 0; i < count; i++) { + GridPaintMask *gpm = &grid_paint_mask[i]; + if (gpm->data) { + const int gridsize = BKE_ccg_gridsize(gpm->level); + BLO_write_raw(writer, sizeof(*gpm->data) * gridsize * gridsize, gpm->data); + } + } + } +} + +void CustomData_blend_write( + BlendWriter *writer, CustomData *data, int count, CustomDataMask cddata_mask, ID *id) +{ + CustomDataLayer *layers = NULL; + CustomDataLayer layers_buff[CD_TEMP_CHUNK_SIZE]; + CustomData_file_write_prepare(data, &layers, layers_buff, ARRAY_SIZE(layers_buff)); + + /* write external customdata (not for undo) */ + if (data->external && !BLO_write_is_undo(writer)) { + CustomData_external_write(data, id, cddata_mask, count, 0); + } + + BLO_write_struct_array_at_address(writer, CustomDataLayer, data->totlayer, data->layers, layers); + + for (int i = 0; i < data->totlayer; i++) { + CustomDataLayer *layer = &layers[i]; + + if (layer->type == CD_MDEFORMVERT) { + /* layer types that allocate own memory need special handling */ + BKE_defvert_blend_write(writer, count, layer->data); + } + else if (layer->type == CD_MDISPS) { + write_mdisps(writer, count, layer->data, layer->flag & CD_FLAG_EXTERNAL); + } + else if (layer->type == CD_PAINT_MASK) { + const float *layer_data = layer->data; + BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); + } + else if (layer->type == CD_SCULPT_FACE_SETS) { + const float *layer_data = layer->data; + BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); + } + else if (layer->type == CD_GRID_PAINT_MASK) { + write_grid_paint_mask(writer, count, layer->data); + } + else if (layer->type == CD_FACEMAP) { + const int *layer_data = layer->data; + BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); + } + else { + const char *structname; + int structnum; + CustomData_file_write_info(layer->type, &structname, &structnum); + if (structnum) { + int datasize = structnum * count; + BLO_write_struct_array_by_name(writer, structname, datasize, layer->data); + } + else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */ + printf("%s error: layer '%s':%d - can't be written to file\n", + __func__, + structname, + layer->type); + } + } + } + + if (data->external) { + BLO_write_struct(writer, CustomDataExternal, data->external); + } + + if (!ELEM(layers, NULL, layers_buff)) { + MEM_freeN(layers); + } +} + +static void blend_read_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external) +{ + if (mdisps) { + for (int i = 0; i < count; i++) { + BLO_read_data_address(reader, &mdisps[i].disps); + BLO_read_data_address(reader, &mdisps[i].hidden); + + if (mdisps[i].totdisp && !mdisps[i].level) { + /* this calculation is only correct for loop mdisps; + * if loading pre-BMesh face mdisps this will be + * overwritten with the correct value in + * bm_corners_to_loops() */ + float gridsize = sqrtf(mdisps[i].totdisp); + mdisps[i].level = (int)(logf(gridsize - 1.0f) / (float)M_LN2) + 1; + } + + if (BLO_read_requires_endian_switch(reader) && (mdisps[i].disps)) { + /* DNA_struct_switch_endian doesn't do endian swap for (*disps)[] */ + /* this does swap for data written at write_mdisps() - readfile.c */ + BLI_endian_switch_float_array(*mdisps[i].disps, mdisps[i].totdisp * 3); + } + if (!external && !mdisps[i].disps) { + mdisps[i].totdisp = 0; + } + } + } +} + +static void blend_read_paint_mask(BlendDataReader *reader, + int count, + GridPaintMask *grid_paint_mask) +{ + if (grid_paint_mask) { + for (int i = 0; i < count; i++) { + GridPaintMask *gpm = &grid_paint_mask[i]; + if (gpm->data) { + BLO_read_data_address(reader, &gpm->data); + } + } + } +} + +void CustomData_blend_read(BlendDataReader *reader, CustomData *data, int count) +{ + BLO_read_data_address(reader, &data->layers); + + /* annoying workaround for bug [#31079] loading legacy files with + * no polygons _but_ have stale customdata */ + if (UNLIKELY(count == 0 && data->layers == NULL && data->totlayer != 0)) { + CustomData_reset(data); + return; + } + + BLO_read_data_address(reader, &data->external); + + int i = 0; + while (i < data->totlayer) { + CustomDataLayer *layer = &data->layers[i]; + + if (layer->flag & CD_FLAG_EXTERNAL) { + layer->flag &= ~CD_FLAG_IN_MEMORY; + } + + layer->flag &= ~CD_FLAG_NOFREE; + + if (CustomData_verify_versions(data, i)) { + BLO_read_data_address(reader, &layer->data); + if (layer->type == CD_MDISPS) { + blend_read_mdisps(reader, count, layer->data, layer->flag & CD_FLAG_EXTERNAL); + } + else if (layer->type == CD_GRID_PAINT_MASK) { + blend_read_paint_mask(reader, count, layer->data); + } + i++; + } + } + + CustomData_update_typemap(data); +} diff --git a/source/blender/blenkernel/intern/deform.c b/source/blender/blenkernel/intern/deform.c index 1a32deac776..ea5e4ec6532 100644 --- a/source/blender/blenkernel/intern/deform.c +++ b/source/blender/blenkernel/intern/deform.c @@ -50,6 +50,8 @@ #include "BKE_object.h" #include "BKE_object_deform.h" +#include "BLO_read_write.h" + #include "data_transfer_intern.h" bDeformGroup *BKE_object_defgroup_new(Object *ob, const char *name) @@ -1523,3 +1525,49 @@ void BKE_defvert_weight_to_rgb(float r_rgb[3], const float weight) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name .blend file I/O + * \{ */ + +void BKE_defvert_blend_write(BlendWriter *writer, int count, MDeformVert *dvlist) +{ + if (dvlist == NULL) { + return; + } + + /* Write the dvert list */ + BLO_write_struct_array(writer, MDeformVert, count, dvlist); + + /* Write deformation data for each dvert */ + for (int i = 0; i < count; i++) { + if (dvlist[i].dw) { + BLO_write_struct_array(writer, MDeformWeight, dvlist[i].totweight, dvlist[i].dw); + } + } +} + +void BKE_defvert_blend_read(BlendDataReader *reader, int count, MDeformVert *mdverts) +{ + if (mdverts == NULL) { + return; + } + + for (int i = count; i > 0; i--, mdverts++) { + /*convert to vgroup allocation system*/ + MDeformWeight *dw; + if (mdverts->dw && (dw = BLO_read_get_new_data_address(reader, mdverts->dw))) { + const size_t dw_len = MAX2(mdverts->totweight, 0) * sizeof(MDeformWeight); + void *dw_tmp = MEM_mallocN(dw_len, __func__); + memcpy(dw_tmp, dw, dw_len); + mdverts->dw = dw_tmp; + MEM_freeN(dw); + } + else { + mdverts->dw = NULL; + mdverts->totweight = 0; + } + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index abed90e7192..294e6f8c8a0 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -48,6 +48,8 @@ #include "BKE_lib_query.h" #include "BKE_nla.h" +#include "BLO_read_write.h" + #include "RNA_access.h" #include "CLG_log.h" @@ -57,13 +59,21 @@ static CLG_LogRef LOG = {"bke.fcurve"}; -/* ************************** Data-Level Functions ************************* */ +/* -------------------------------------------------------------------- */ +/** \name F-Curve Data Create + * \{ */ + FCurve *BKE_fcurve_create(void) { FCurve *fcu = MEM_callocN(sizeof(FCurve), __func__); return fcu; } -/* ---------------------- Freeing --------------------------- */ + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve Data Free + * \{ */ /* Frees the F-Curve itself too, so make sure BLI_remlink is called before calling this... */ void BKE_fcurve_free(FCurve *fcu) @@ -110,7 +120,11 @@ void BKE_fcurves_free(ListBase *list) BLI_listbase_clear(list); } -/* ---------------------- Copy --------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve Data Copy + * \{ */ /* duplicate an F-Curve */ FCurve *BKE_fcurve_copy(const FCurve *fcu) @@ -478,7 +492,11 @@ FCurve *BKE_fcurve_find_by_rna_context_ui(bContext *C, return fcu; } -/* ----------------- Finding Keyframes/Extents -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Finding Keyframes/Extents + * \{ */ /* Binary search algorithm for finding where to insert BezTriple, * with optional argument for precision required. @@ -809,7 +827,11 @@ bool BKE_fcurve_calc_range( return foundvert; } -/* ----------------- Status Checks -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Status Checks + * \{ */ /* Are keyframes on F-Curve of any use? * Usability of keyframes refers to whether they should be displayed, @@ -899,7 +921,11 @@ bool BKE_fcurve_is_keyframable(FCurve *fcu) return true; } -/* ***************************** Keyframe Column Tools ********************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Keyframe Column Tools + * \{ */ /* add a BezTriple to a column */ void bezt_add_to_cfra_elem(ListBase *lb, BezTriple *bezt) @@ -933,7 +959,12 @@ void bezt_add_to_cfra_elem(ListBase *lb, BezTriple *bezt) cen->sel = bezt->f2; } -/* ***************************** Samples Utilities ******************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Samples Utilities + * \{ */ + /* Some utilities for working with FPoints (i.e. 'sampled' animation curve data, such as * data imported from BVH/Mocap files), which are specialized for use with high density datasets, * which BezTriples/Keyframe data are ill equipped to do. @@ -1271,7 +1302,11 @@ short test_time_fcurve(FCurve *fcu) return 0; } -/* ***************************** Curve Calculations ********************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve Calculations + * \{ */ /* The total length of the handles is not allowed to be more * than the horizontal distance between (v1-v4). @@ -1449,7 +1484,11 @@ static void berekeny(float f1, float f2, float f3, float f4, float *o, int b) } } -/* -------------------------- */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve Evaluation + * \{ */ static float fcurve_eval_keyframes_extrapolate( FCurve *fcu, BezTriple *bezts, float evaltime, int endpoint_offset, int direction_to_neighbor) @@ -1812,7 +1851,11 @@ static float fcurve_eval_samples(FCurve *fcu, FPoint *fpts, float evaltime) return cvalue; } -/* ***************************** F-Curve - Evaluation ********************************* */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve - Evaluation + * \{ */ /* Evaluate and return the value of the given F-Curve at the specified frame ("evaltime") * Note: this is also used for drivers @@ -1947,3 +1990,268 @@ float calculate_fcurve(PathResolvedRNA *anim_rna, fcu->curval = curval; /* debug display only, not thread safe! */ return curval; } + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name F-Curve - .blend file API + * \{ */ + +void BKE_fmodifiers_blend_write(BlendWriter *writer, ListBase *fmodifiers) +{ + /* Write all modifiers first (for faster reloading) */ + BLO_write_struct_list(writer, FModifier, fmodifiers); + + /* Modifiers */ + LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) { + const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); + + /* Write the specific data */ + if (fmi && fcm->data) { + /* firstly, just write the plain fmi->data struct */ + BLO_write_struct_by_name(writer, fmi->structName, fcm->data); + + /* do any modifier specific stuff */ + switch (fcm->type) { + case FMODIFIER_TYPE_GENERATOR: { + FMod_Generator *data = fcm->data; + + /* write coefficients array */ + if (data->coefficients) { + BLO_write_float_array(writer, data->arraysize, data->coefficients); + } + + break; + } + case FMODIFIER_TYPE_ENVELOPE: { + FMod_Envelope *data = fcm->data; + + /* write envelope data */ + if (data->data) { + BLO_write_struct_array(writer, FCM_EnvelopeData, data->totvert, data->data); + } + + break; + } + case FMODIFIER_TYPE_PYTHON: { + FMod_Python *data = fcm->data; + + /* Write ID Properties -- and copy this comment EXACTLY for easy finding + * of library blocks that implement this.*/ + IDP_BlendWrite(writer, data->prop); + + break; + } + } + } + } +} + +void BKE_fmodifiers_blend_read_data(BlendDataReader *reader, ListBase *fmodifiers, FCurve *curve) +{ + LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) { + /* relink general data */ + BLO_read_data_address(reader, &fcm->data); + fcm->curve = curve; + + /* do relinking of data for specific types */ + switch (fcm->type) { + case FMODIFIER_TYPE_GENERATOR: { + FMod_Generator *data = (FMod_Generator *)fcm->data; + BLO_read_float_array(reader, data->arraysize, &data->coefficients); + break; + } + case FMODIFIER_TYPE_ENVELOPE: { + FMod_Envelope *data = (FMod_Envelope *)fcm->data; + + BLO_read_data_address(reader, &data->data); + + break; + } + case FMODIFIER_TYPE_PYTHON: { + FMod_Python *data = (FMod_Python *)fcm->data; + + BLO_read_data_address(reader, &data->prop); + IDP_BlendDataRead(reader, &data->prop); + + break; + } + } + } +} + +void BKE_fmodifiers_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *fmodifiers) +{ + LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) { + /* data for specific modifiers */ + switch (fcm->type) { + case FMODIFIER_TYPE_PYTHON: { + FMod_Python *data = (FMod_Python *)fcm->data; + BLO_read_id_address(reader, id->lib, &data->script); + break; + } + } + } +} + +void BKE_fmodifiers_blend_read_expand(BlendExpander *expander, ListBase *fmodifiers) +{ + LISTBASE_FOREACH (FModifier *, fcm, fmodifiers) { + /* library data for specific F-Modifier types */ + switch (fcm->type) { + case FMODIFIER_TYPE_PYTHON: { + FMod_Python *data = (FMod_Python *)fcm->data; + BLO_expand(expander, data->script); + break; + } + } + } +} + +void BKE_fcurve_blend_write(BlendWriter *writer, ListBase *fcurves) +{ + BLO_write_struct_list(writer, FCurve, fcurves); + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + /* curve data */ + if (fcu->bezt) { + BLO_write_struct_array(writer, BezTriple, fcu->totvert, fcu->bezt); + } + if (fcu->fpt) { + BLO_write_struct_array(writer, FPoint, fcu->totvert, fcu->fpt); + } + + if (fcu->rna_path) { + BLO_write_string(writer, fcu->rna_path); + } + + /* driver data */ + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + + BLO_write_struct(writer, ChannelDriver, driver); + + /* variables */ + BLO_write_struct_list(writer, DriverVar, &driver->variables); + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { + if (dtar->rna_path) { + BLO_write_string(writer, dtar->rna_path); + } + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* write F-Modifiers */ + BKE_fmodifiers_blend_write(writer, &fcu->modifiers); + } +} + +void BKE_fcurve_blend_read_data(BlendDataReader *reader, ListBase *fcurves) +{ + /* link F-Curve data to F-Curve again (non ID-libs) */ + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + /* curve data */ + BLO_read_data_address(reader, &fcu->bezt); + BLO_read_data_address(reader, &fcu->fpt); + + /* rna path */ + BLO_read_data_address(reader, &fcu->rna_path); + + /* group */ + BLO_read_data_address(reader, &fcu->grp); + + /* clear disabled flag - allows disabled drivers to be tried again ([#32155]), + * but also means that another method for "reviving disabled F-Curves" exists + */ + fcu->flag &= ~FCURVE_DISABLED; + + /* driver */ + BLO_read_data_address(reader, &fcu->driver); + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + + /* Compiled expression data will need to be regenerated + * (old pointer may still be set here). */ + driver->expr_comp = NULL; + driver->expr_simple = NULL; + + /* give the driver a fresh chance - the operating environment may be different now + * (addons, etc. may be different) so the driver namespace may be sane now [#32155] + */ + driver->flag &= ~DRIVER_FLAG_INVALID; + + /* relink variables, targets and their paths */ + BLO_read_list(reader, &driver->variables); + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + /* only relink the targets being used */ + if (tarIndex < dvar->num_targets) { + BLO_read_data_address(reader, &dtar->rna_path); + } + else { + dtar->rna_path = NULL; + } + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* modifiers */ + BLO_read_list(reader, &fcu->modifiers); + BKE_fmodifiers_blend_read_data(reader, &fcu->modifiers, fcu); + } +} + +void BKE_fcurve_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *fcurves) +{ + if (fcurves == NULL) { + return; + } + + /* relink ID-block references... */ + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + /* driver data */ + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + /* only relink if still used */ + if (tarIndex < dvar->num_targets) { + BLO_read_id_address(reader, id->lib, &dtar->id); + } + else { + dtar->id = NULL; + } + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* modifiers */ + BKE_fmodifiers_blend_read_lib(reader, id, &fcu->modifiers); + } +} + +void BKE_fcurve_blend_read_expand(BlendExpander *expander, ListBase *fcurves) +{ + LISTBASE_FOREACH (FCurve *, fcu, fcurves) { + /* Driver targets if there is a driver */ + if (fcu->driver) { + ChannelDriver *driver = fcu->driver; + + LISTBASE_FOREACH (DriverVar *, dvar, &driver->variables) { + DRIVER_TARGETS_LOOPER_BEGIN (dvar) { + // TODO: only expand those that are going to get used? + BLO_expand(expander, dtar->id); + } + DRIVER_TARGETS_LOOPER_END; + } + } + + /* F-Curve Modifiers */ + BKE_fmodifiers_blend_read_expand(expander, &fcu->modifiers); + } +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/font.c index 4bedba8f76b..dfbb8202093 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/font.c @@ -135,6 +135,12 @@ IDTypeInfo IDType_ID_VF = { .free_data = vfont_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /***************************** VFont *******************************/ diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 4f65f8a57ab..10b6328ead4 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -126,6 +126,12 @@ IDTypeInfo IDType_ID_GD = { .free_data = greasepencil_free_data, .make_local = NULL, .foreach_id = greasepencil_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* ************************************************** */ diff --git a/source/blender/blenkernel/intern/gpencil_geom.c b/source/blender/blenkernel/intern/gpencil_geom.c index 65ce117431b..4226e65d8fa 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.c +++ b/source/blender/blenkernel/intern/gpencil_geom.c @@ -2238,12 +2238,12 @@ static Material *gpencil_add_material(Main *bmain, gp_style->flag |= GP_MATERIAL_STROKE_SHOW; } else { - linearrgb_to_srgb_v4(gp_style->stroke_rgba, color); + copy_v4_v4(gp_style->stroke_rgba, color); gp_style->flag &= ~GP_MATERIAL_STROKE_SHOW; } /* Fill color. */ - linearrgb_to_srgb_v4(gp_style->fill_rgba, color); + copy_v4_v4(gp_style->fill_rgba, color); if (use_fill) { gp_style->flag |= GP_MATERIAL_FILL_SHOW; } diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index 2905bfc978a..8831d09698b 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -119,6 +119,12 @@ IDTypeInfo IDType_ID_HA = { .free_data = hair_free_data, .make_local = NULL, .foreach_id = hair_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static void hair_random(Hair *hair) diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 529ae227df9..661d27f4765 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -26,11 +26,13 @@ #include <stdlib.h> #include <string.h> +#include "BLI_endian_switch.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BKE_global.h" #include "BKE_idprop.h" #include "BKE_lib_id.h" @@ -38,6 +40,8 @@ #include "MEM_guardedalloc.h" +#include "BLO_read_write.h" + #include "BLI_strict_flags.h" /* IDPropertyTemplate is a union in DNA_ID.h */ @@ -1170,4 +1174,270 @@ void IDP_foreach_property(IDProperty *id_property_root, } } +void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer); + +static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer) +{ + /*REMEMBER to set totalen to len in the linking code!!*/ + if (prop->data.pointer) { + BLO_write_raw(writer, (int)MEM_allocN_len(prop->data.pointer), prop->data.pointer); + + if (prop->subtype == IDP_GROUP) { + IDProperty **array = prop->data.pointer; + int a; + + for (a = 0; a < prop->len; a++) { + IDP_BlendWrite(writer, array[a]); + } + } + } +} + +static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer) +{ + /*REMEMBER to set totalen to len in the linking code!!*/ + if (prop->data.pointer) { + const IDProperty *array = prop->data.pointer; + int a; + + BLO_write_struct_array(writer, IDProperty, prop->len, array); + + for (a = 0; a < prop->len; a++) { + IDP_WriteProperty_OnlyData(&array[a], writer); + } + } +} + +static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer) +{ + /*REMEMBER to set totalen to len in the linking code!!*/ + BLO_write_raw(writer, prop->len, prop->data.pointer); +} + +static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer) +{ + IDProperty *loop; + + for (loop = prop->data.group.first; loop; loop = loop->next) { + IDP_BlendWrite(writer, loop); + } +} + +/* Functions to read/write ID Properties */ +void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer) +{ + switch (prop->type) { + case IDP_GROUP: + IDP_WriteGroup(prop, writer); + break; + case IDP_STRING: + IDP_WriteString(prop, writer); + break; + case IDP_ARRAY: + IDP_WriteArray(prop, writer); + break; + case IDP_IDPARRAY: + IDP_WriteIDPArray(prop, writer); + break; + } +} + +void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop) +{ + BLO_write_struct(writer, IDProperty, prop); + IDP_WriteProperty_OnlyData(prop, writer); +} + +static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader); + +static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader) +{ + IDProperty *array; + int i; + + /* since we didn't save the extra buffer, set totallen to len */ + prop->totallen = prop->len; + BLO_read_data_address(reader, &prop->data.pointer); + + array = (IDProperty *)prop->data.pointer; + + /* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared + * there's not really anything we can do to correct this, at least don't crash */ + if (array == NULL) { + prop->len = 0; + prop->totallen = 0; + } + + for (i = 0; i < prop->len; i++) { + IDP_DirectLinkProperty(&array[i], reader); + } +} + +static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader) +{ + IDProperty **array; + int i; + + /* since we didn't save the extra buffer, set totallen to len */ + prop->totallen = prop->len; + + if (prop->subtype == IDP_GROUP) { + BLO_read_pointer_array(reader, &prop->data.pointer); + array = prop->data.pointer; + + for (i = 0; i < prop->len; i++) { + IDP_DirectLinkProperty(array[i], reader); + } + } + else if (prop->subtype == IDP_DOUBLE) { + BLO_read_double_array(reader, prop->len, (double **)&prop->data.pointer); + } + else { + /* also used for floats */ + BLO_read_int32_array(reader, prop->len, (int **)&prop->data.pointer); + } +} + +static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader) +{ + /*since we didn't save the extra string buffer, set totallen to len.*/ + prop->totallen = prop->len; + BLO_read_data_address(reader, &prop->data.pointer); +} + +static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader) +{ + ListBase *lb = &prop->data.group; + IDProperty *loop; + + BLO_read_list(reader, lb); + + /*Link child id properties now*/ + for (loop = prop->data.group.first; loop; loop = loop->next) { + IDP_DirectLinkProperty(loop, reader); + } +} + +static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader) +{ + switch (prop->type) { + case IDP_GROUP: + IDP_DirectLinkGroup(prop, reader); + break; + case IDP_STRING: + IDP_DirectLinkString(prop, reader); + break; + case IDP_ARRAY: + IDP_DirectLinkArray(prop, reader); + break; + case IDP_IDPARRAY: + IDP_DirectLinkIDPArray(prop, reader); + break; + case IDP_DOUBLE: + /* Workaround for doubles. + * They are stored in the same field as `int val, val2` in the #IDPropertyData struct, + * they have to deal with endianness specifically. + * + * In theory, val and val2 would've already been swapped + * if switch_endian is true, so we have to first un-swap + * them then re-swap them as a single 64-bit entity. */ + if (BLO_read_requires_endian_switch(reader)) { + BLI_endian_switch_int32(&prop->data.val); + BLI_endian_switch_int32(&prop->data.val2); + BLI_endian_switch_int64((int64_t *)&prop->data.val); + } + break; + case IDP_INT: + case IDP_FLOAT: + case IDP_ID: + break; /* Nothing special to do here. */ + default: + /* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code, + * IDP are way too polymorphic to do it safely. */ + printf( + "%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type); + /* Note: we do not attempt to free unknown prop, we have no way to know how to do that! */ + prop->type = IDP_INT; + prop->subtype = 0; + IDP_Int(prop) = 0; + } +} + +void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const char *caller_func_id) +{ + if (*prop) { + if ((*prop)->type == IDP_GROUP) { + IDP_DirectLinkGroup(*prop, reader); + } + else { + /* corrupt file! */ + printf("%s: found non group data, freeing type %d!\n", caller_func_id, (*prop)->type); + /* don't risk id, data's likely corrupt. */ + // IDP_FreePropertyContent(*prop); + *prop = NULL; + } + } +} + +void IDP_BlendReadLib(BlendLibReader *reader, IDProperty *prop) +{ + if (!prop) { + return; + } + + switch (prop->type) { + case IDP_ID: /* PointerProperty */ + { + void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop)); + if (IDP_Id(prop) && !newaddr && G.debug) { + printf("Error while loading \"%s\". Data not found in file!\n", prop->name); + } + prop->data.pointer = newaddr; + break; + } + case IDP_IDPARRAY: /* CollectionProperty */ + { + IDProperty *idp_array = IDP_IDPArray(prop); + for (int i = 0; i < prop->len; i++) { + IDP_BlendReadLib(reader, &(idp_array[i])); + } + break; + } + case IDP_GROUP: /* PointerProperty */ + { + LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { + IDP_BlendReadLib(reader, loop); + } + break; + } + default: + break; /* Nothing to do for other IDProps. */ + } +} + +void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop) +{ + if (!prop) { + return; + } + + switch (prop->type) { + case IDP_ID: + BLO_expand(expander, IDP_Id(prop)); + break; + case IDP_IDPARRAY: { + IDProperty *idp_array = IDP_IDPArray(prop); + for (int i = 0; i < prop->len; i++) { + IDP_BlendReadExpand(expander, &idp_array[i]); + } + break; + } + case IDP_GROUP: + LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { + IDP_BlendReadExpand(expander, loop); + } + break; + } +} + /** \} */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 7ff34acca7a..0f694a26b45 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -232,6 +232,11 @@ IDTypeInfo IDType_ID_IM = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = image_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* prototypes */ diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 94a142600b6..3e92fd13370 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -124,6 +124,12 @@ IDTypeInfo IDType_ID_IP = { .free_data = ipo_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* *************************************************** */ @@ -2197,7 +2203,7 @@ void do_versions_ipos_to_animato(Main *bmain) AnimData *adt = BKE_animdata_add_id(id); - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { IpoCurve *icu = (seq->ipo) ? seq->ipo->curve.first : NULL; short adrcode = SEQ_FAC1; @@ -2238,7 +2244,7 @@ void do_versions_ipos_to_animato(Main *bmain) id_us_min(&seq->ipo->id); seq->ipo = NULL; } - SEQ_END; + SEQ_ALL_END; } } diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 0108befa710..456325851a6 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -113,6 +113,12 @@ IDTypeInfo IDType_ID_KE = { .free_data = shapekey_free_data, .make_local = NULL, .foreach_id = shapekey_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; #define KEY_MODE_DUMMY 0 /* use where mode isn't checked for */ diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index 4d073593da7..7304dd91eea 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -35,6 +35,9 @@ #include "BLT_translation.h" +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + #include "DNA_curve_types.h" #include "DNA_defaults.h" #include "DNA_key_types.h" @@ -43,7 +46,9 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_anim_data.h" #include "BKE_curve.h" +#include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_idtype.h" #include "BKE_lattice.h" @@ -53,10 +58,10 @@ #include "BKE_modifier.h" #include "BKE_object.h" -#include "BKE_deform.h" - #include "DEG_depsgraph_query.h" +#include "BLO_read_write.h" + static void lattice_init_data(ID *id) { Lattice *lattice = (Lattice *)id; @@ -124,6 +129,59 @@ static void lattice_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS(data, lattice->key, IDWALK_CB_USER); } +static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Lattice *lt = (Lattice *)id; + if (lt->id.us > 0 || BLO_write_is_undo(writer)) { + /* Clean up, important in undo case to reduce false detection of changed datablocks. */ + lt->editlatt = NULL; + lt->batch_cache = NULL; + + /* write LibData */ + BLO_write_id_struct(writer, Lattice, id_address, <->id); + BKE_id_blend_write(writer, <->id); + + /* write animdata */ + if (lt->adt) { + BKE_animdata_blend_write(writer, lt->adt); + } + + /* direct data */ + BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); + + BKE_defvert_blend_write(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); + } +} + +static void lattice_blend_read_data(BlendDataReader *reader, ID *id) +{ + Lattice *lt = (Lattice *)id; + BLO_read_data_address(reader, <->def); + + BLO_read_data_address(reader, <->dvert); + BKE_defvert_blend_read(reader, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); + + lt->editlatt = NULL; + lt->batch_cache = NULL; + + BLO_read_data_address(reader, <->adt); + BKE_animdata_blend_read_data(reader, lt->adt); +} + +static void lattice_blend_read_lib(BlendLibReader *reader, ID *id) +{ + Lattice *lt = (Lattice *)id; + BLO_read_id_address(reader, lt->id.lib, <->ipo); // XXX deprecated - old animation system + BLO_read_id_address(reader, lt->id.lib, <->key); +} + +static void lattice_blend_read_expand(BlendExpander *expander, ID *id) +{ + Lattice *lt = (Lattice *)id; + BLO_expand(expander, lt->ipo); // XXX deprecated - old animation system + BLO_expand(expander, lt->key); +} + IDTypeInfo IDType_ID_LT = { .id_code = ID_LT, .id_filter = FILTER_ID_LT, @@ -139,6 +197,12 @@ IDTypeInfo IDType_ID_LT = { .free_data = lattice_free_data, .make_local = NULL, .foreach_id = lattice_foreach_id, + .foreach_cache = NULL, + + .blend_write = lattice_blend_write, + .blend_read_data = lattice_blend_read_data, + .blend_read_lib = lattice_blend_read_lib, + .blend_read_expand = lattice_blend_read_expand, }; int BKE_lattice_index_from_uvw(Lattice *lt, const int u, const int v, const int w) diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 3b7aae98327..dfe939aa878 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -75,6 +75,8 @@ #include "RNA_access.h" +#include "BLO_read_write.h" + #include "atomic_ops.h" //#define DEBUG_TIME @@ -342,8 +344,11 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) } if (cb_flag & IDWALK_CB_EMBEDDED) { - /* Embedded data-blocks need to be made fully local as well. */ - if (*id_pointer != NULL) { + /* Embedded data-blocks need to be made fully local as well. + * Note however that in some cases (when owner ID had to be duplicated instead of being made + * local directly), its embedded IDs should also have already been duplicated, and hence be + * fully local here already. */ + if (*id_pointer != NULL && ID_IS_LINKED(*id_pointer)) { BLI_assert(*id_pointer != id_self); lib_id_clear_library_data_ex(bmain, *id_pointer); @@ -640,7 +645,7 @@ ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplica BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags); if (key_new != NULL) { - BKE_animdata_duplicate_id_action(bmain, id_new, duplicate_flags); + BKE_animdata_duplicate_id_action(bmain, key_new, duplicate_flags); } /* Note that actions of embedded data (root nodetrees and master collections) are handled * by `BKE_animdata_duplicate_id_action` as well. */ @@ -2326,3 +2331,30 @@ void BKE_id_reorder(const ListBase *lb, ID *id, ID *relative, bool after) *id_order = relative_order - 1; } } + +void BKE_id_blend_write(BlendWriter *writer, ID *id) +{ + /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */ + if (id->properties && !ELEM(GS(id->name), ID_WM)) { + IDP_BlendWrite(writer, id->properties); + } + + if (id->override_library) { + BLO_write_struct(writer, IDOverrideLibrary, id->override_library); + + BLO_write_struct_list(writer, IDOverrideLibraryProperty, &id->override_library->properties); + LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { + BLO_write_string(writer, op->rna_path); + + BLO_write_struct_list(writer, IDOverrideLibraryPropertyOperation, &op->operations); + LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { + if (opop->subitem_reference_name) { + BLO_write_string(writer, opop->subitem_reference_name); + } + if (opop->subitem_local_name) { + BLO_write_string(writer, opop->subitem_local_name); + } + } + } + } +} diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 5b45148ed63..e9244c5af73 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -361,7 +361,10 @@ bool BKE_lib_override_library_create_from_tag(Main *bmain) return success; } -static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint tag) +static bool lib_override_hierarchy_recursive_tag(Main *bmain, + ID *id, + const uint tag, + Library *override_group_lib_reference) { void **entry_vp = BLI_ghash_lookup_p(bmain->relations->id_user_to_used, id); if (entry_vp == NULL) { @@ -369,6 +372,11 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint return (id->tag & tag) != 0; } + if (override_group_lib_reference != NULL && ID_IS_OVERRIDE_LIBRARY_REAL(id) && + id->override_library->reference->lib == override_group_lib_reference) { + id->tag |= tag; + } + /* This way we won't process again that ID should we encounter it again through another * relationship hierarchy. * Note that this does not free any memory from relations, so we can still use the entries. @@ -383,7 +391,9 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint } /* We only consider IDs from the same library. */ if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) { - if (lib_override_hierarchy_recursive_tag(bmain, *entry->id_pointer, tag)) { + if (lib_override_hierarchy_recursive_tag( + bmain, *entry->id_pointer, tag, override_group_lib_reference) && + override_group_lib_reference == NULL) { id->tag |= tag; } } @@ -395,6 +405,7 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint /** * Tag all IDs in given \a bmain that are being used by given \a id_root ID or its dependencies, * recursively. + * It detects and tag only chains of dependencies marked at both ends by given tag. * * This will include all local IDs, and all IDs from the same library as the \a id_root. * @@ -402,8 +413,8 @@ static bool lib_override_hierarchy_recursive_tag(Main *bmain, ID *id, const uint * \param do_create_main_relashionships Whether main relations needs to be created or already exist * (in any case, they will be freed by this function). */ -void BKE_lib_override_library_dependencies_tag(struct Main *bmain, - struct ID *id_root, +void BKE_lib_override_library_dependencies_tag(Main *bmain, + ID *id_root, const uint tag, const bool do_create_main_relashionships) { @@ -411,10 +422,36 @@ void BKE_lib_override_library_dependencies_tag(struct Main *bmain, BKE_main_relations_create(bmain, 0); } - /* Then we tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey + /* We tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey * has a driver using an armature object's bone, we need to override the shapekey/obdata, the * objects using them, etc.) */ - lib_override_hierarchy_recursive_tag(bmain, id_root, tag); + lib_override_hierarchy_recursive_tag(bmain, id_root, tag, NULL); + + BKE_main_relations_free(bmain); +} + +/** + * Tag all IDs in given \a bmain that are part of the same \a id_root liboverride ID group. + * That is, all other liboverrides IDs (in)directly used by \a is_root one, sharing the same + * library for their reference IDs. + * + * \param id_root The root of the hierarchy of liboverride dependencies to be tagged. + * \param do_create_main_relashionships Whether main relations needs to be created or already exist + * (in any case, they will be freed by this function). + */ +void BKE_lib_override_library_override_group_tag(Main *bmain, + ID *id_root, + const uint tag, + const bool do_create_main_relashionships) +{ + if (do_create_main_relashionships) { + BKE_main_relations_create(bmain, 0); + } + + /* We tag all liboverride data-blocks from the same library as reference one, + * being used by the root ID. */ + lib_override_hierarchy_recursive_tag( + bmain, id_root, tag, id_root->override_library->reference->lib); BKE_main_relations_free(bmain); } @@ -459,26 +496,7 @@ static int lib_override_library_make_tag_ids_cb(LibraryIDLinkCallbackData *cb_da return IDWALK_RET_NOP; } -/** - * Advanced 'smart' function to create fully functional overrides. - * - * \note Currently it only does special things if given \a id_root is an object of collection, more - * specific behaviors may be added in the future for other ID types. - * - * \note It will overrides all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at - * its beginning, so caller code can add extra data-blocks to be overridden as well. - * - * \note In the future that same function may be extended to support 'refresh' of overrides - * (rebuilding overrides from linked data, trying to preserve local overrides already defined). - * - * \param id_root The root ID to create an override from. - * \param id_reference some reference ID used to do some post-processing after overrides have been - * created, may be NULL. Typically, the Empty object instantiating the linked - * collection we override, currently. - * \return true if override was successfully created. - */ -bool BKE_lib_override_library_create( - Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference) +static bool lib_override_library_create_do(Main *bmain, ID *id_root) { /* Tag all collections and objects, as well as other IDs using them. */ id_root->tag |= LIB_TAG_DOIT; @@ -508,115 +526,308 @@ bool BKE_lib_override_library_create( /* Note that this call will also free the main relations data we created above. */ BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, false); - const bool success = BKE_lib_override_library_create_from_tag(bmain); - - if (success) { - BKE_main_collection_sync(bmain); - - switch (GS(id_root->name)) { - case ID_GR: { - Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? - (Object *)id_reference : - NULL; - Collection *collection_new = ((Collection *)id_root->newid); - if (ob_reference != NULL) { - BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); - } - else { - BKE_collection_add_from_collection( - bmain, scene, ((Collection *)id_root), collection_new); - } + return BKE_lib_override_library_create_from_tag(bmain); +} - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) { - if (ob_new != NULL && ob_new->id.override_library != NULL) { - if (ob_reference != NULL) { - Base *base; - if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) { - BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new); - base = BKE_view_layer_base_find(view_layer, ob_new); - DEG_id_tag_update_ex( - bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); - } +static void lib_override_library_create_post_process( + Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference) +{ + BKE_main_collection_sync(bmain); + + switch (GS(id_root->name)) { + case ID_GR: { + Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ? + (Object *)id_reference : + NULL; + Collection *collection_new = ((Collection *)id_root->newid); + if (ob_reference != NULL) { + BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new); + } + else { + BKE_collection_add_from_collection(bmain, scene, ((Collection *)id_root), collection_new); + } - if (ob_new == (Object *)ob_reference->id.newid) { - /* TODO: is setting active needed? */ - BKE_view_layer_base_select_and_set_active(view_layer, base); - } - } - else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { - BKE_collection_object_add(bmain, collection_new, ob_new); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) { + if (ob_new != NULL && ob_new->id.override_library != NULL) { + if (ob_reference != NULL) { + Base *base; + if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) { + BKE_collection_object_add_from(bmain, scene, ob_reference, ob_new); + base = BKE_view_layer_base_find(view_layer, ob_new); DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } + + if (ob_new == (Object *)ob_reference->id.newid) { + /* TODO: is setting active needed? */ + BKE_view_layer_base_select_and_set_active(view_layer, base); + } + } + else if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { + BKE_collection_object_add(bmain, collection_new, ob_new); + DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); } } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - break; - } - case ID_OB: { - BKE_collection_object_add_from( - bmain, scene, (Object *)id_root, ((Object *)id_root->newid)); - break; } - default: - break; + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + break; + } + case ID_OB: { + BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ((Object *)id_root->newid)); + break; } + default: + break; + } - /* We need to ensure all new overrides of objects are properly instantiated. */ - LISTBASE_FOREACH (Object *, ob, &bmain->objects) { - Object *ob_new = (Object *)ob->id.newid; - if (ob_new != NULL) { - BLI_assert(ob_new->id.override_library != NULL && - ob_new->id.override_library->reference == &ob->id); - - Collection *default_instantiating_collection = NULL; - if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { - if (default_instantiating_collection == NULL) { - switch (GS(id_root->name)) { - case ID_GR: { - default_instantiating_collection = BKE_collection_add( - bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); - break; - } - case ID_OB: { - /* Add the new container collection to one of the collections instantiating the - * root object, or scene's master collection if none found. */ - Object *ob_root = (Object *)id_root; - LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { - if (BKE_collection_has_object(collection, ob_root) && - BKE_view_layer_has_collection(view_layer, collection) && - !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { - default_instantiating_collection = BKE_collection_add( - bmain, collection, "OVERRIDE_HIDDEN"); - } - } - if (default_instantiating_collection == NULL) { + /* We need to ensure all new overrides of objects are properly instantiated. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + Object *ob_new = (Object *)ob->id.newid; + if (ob_new != NULL) { + BLI_assert(ob_new->id.override_library != NULL && + ob_new->id.override_library->reference == &ob->id); + + Collection *default_instantiating_collection = NULL; + if (BKE_view_layer_base_find(view_layer, ob_new) == NULL) { + if (default_instantiating_collection == NULL) { + switch (GS(id_root->name)) { + case ID_GR: { + default_instantiating_collection = BKE_collection_add( + bmain, (Collection *)id_root, "OVERRIDE_HIDDEN"); + break; + } + case ID_OB: { + /* Add the new container collection to one of the collections instantiating the + * root object, or scene's master collection if none found. */ + Object *ob_root = (Object *)id_root; + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_has_object(collection, ob_root) && + BKE_view_layer_has_collection(view_layer, collection) && + !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { default_instantiating_collection = BKE_collection_add( - bmain, scene->master_collection, "OVERRIDE_HIDDEN"); + bmain, collection, "OVERRIDE_HIDDEN"); } - break; } - default: - BLI_assert(0); + if (default_instantiating_collection == NULL) { + default_instantiating_collection = BKE_collection_add( + bmain, scene->master_collection, "OVERRIDE_HIDDEN"); + } + break; } - /* Hide the collection from viewport and render. */ - default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | - COLLECTION_RESTRICT_RENDER; + default: + BLI_assert(0); } + /* Hide the collection from viewport and render. */ + default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT | + COLLECTION_RESTRICT_RENDER; + } + + BKE_collection_object_add(bmain, default_instantiating_collection, ob_new); + DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); + } + } + } +} - BKE_collection_object_add(bmain, default_instantiating_collection, ob_new); - DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS); +/** + * Advanced 'smart' function to create fully functional overrides. + * + * \note Currently it only does special things if given \a id_root is an object of collection, more + * specific behaviors may be added in the future for other ID types. + * + * \note It will overrides all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at + * its beginning, so caller code can add extra data-blocks to be overridden as well. + * + * \note In the future that same function may be extended to support 'refresh' of overrides + * (rebuilding overrides from linked data, trying to preserve local overrides already defined). + * + * \param id_root The root ID to create an override from. + * \param id_reference some reference ID used to do some post-processing after overrides have been + * created, may be NULL. Typically, the Empty object instantiating the linked + * collection we override, currently. + * \return true if override was successfully created. + */ +bool BKE_lib_override_library_create( + Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root, ID *id_reference) +{ + const bool success = lib_override_library_create_do(bmain, id_root); + + if (!success) { + return success; + } + + lib_override_library_create_post_process(bmain, scene, view_layer, id_root, id_reference); + + /* Cleanup. */ + BKE_main_id_clear_newpoins(bmain); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + + return success; +} + +/** + * Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked + * data, from an existing override hierarchy. + * + * \param id_root The root liboverride ID to resync from. + * \return true if override was successfully resynced. + */ +bool BKE_lib_override_library_resync(Main *bmain, Scene *scene, ViewLayer *view_layer, ID *id_root) +{ + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + + /* Tag all collections and objects, as well as other IDs using them. */ + id_root->tag |= LIB_TAG_DOIT; + ID *id_root_reference = id_root->override_library->reference; + + /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag + * linked reference ones to be overridden again. */ + BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true); + + GHash *linkedref_to_old_override = BLI_ghash_new( + BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT && ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + /* While this should not happen in typical cases (and won't be properly supported here), user + * is free to do all kind of very bad things, including having different local overrides of a + * same linked ID in a same hierarchy... */ + if (!BLI_ghash_haskey(linkedref_to_old_override, id->override_library->reference)) { + BLI_ghash_insert(linkedref_to_old_override, id->override_library->reference, id); + id->override_library->reference->tag |= LIB_TAG_DOIT; + } + } + } + FOREACH_MAIN_ID_END; + + /* Make new override from linked data. */ + /* Note that this call also remap all pointers of tagged IDs from old override IDs to new + * override IDs (including within the old overrides themselves, since those are tagged too + * above). */ + const bool success = lib_override_library_create_do(bmain, id_root_reference); + + if (!success) { + return success; + } + + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) { + ID *id_override_new = id->newid; + ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + + if (id_override_old != NULL) { + /* Swap the names between old override ID and new one. */ + char id_name_buf[MAX_ID_NAME]; + memcpy(id_name_buf, id_override_old->name, sizeof(id_name_buf)); + memcpy(id_override_old->name, id_override_new->name, sizeof(id_override_old->name)); + memcpy(id_override_new->name, id_name_buf, sizeof(id_override_new->name)); + /* Note that this is very efficient way to keep BMain IDs ordered as expected after + * swapping their names. + * However, one has to be very careful with this when iterating over the listbase at the + * same time. Here it works because we only execute this code when we are in the linked + * IDs, which are always *after* all local ones, and we only affect local IDs. */ + BLI_listbase_swaplinks(lb, id_override_old, id_override_new); + + /* Remap the whole local IDs to use the new override. */ + BKE_libblock_remap( + bmain, id_override_old, id_override_new, ID_REMAP_SKIP_INDIRECT_USAGE); + + /* Copy over overrides rules from old override ID to new one. */ + BLI_duplicatelist(&id_override_new->override_library->properties, + &id_override_old->override_library->properties); + for (IDOverrideLibraryProperty * + op_new = id_override_new->override_library->properties.first, + *op_old = id_override_old->override_library->properties.first; + op_new; + op_new = op_new->next, op_old = op_old->next) { + lib_override_library_property_copy(op_new, op_old); + } + + /* Apply rules on new override ID using old one as 'source' data. */ + /* Note that since we already remapped ID pointers in old override IDs to new ones, we + * can also apply ID pointer override rules safely here. */ + PointerRNA rnaptr_src, rnaptr_dst; + RNA_id_pointer_create(id_override_old, &rnaptr_src); + RNA_id_pointer_create(id_override_new, &rnaptr_dst); + + RNA_struct_override_apply( + bmain, &rnaptr_dst, &rnaptr_src, NULL, id_override_new->override_library); } } } + FOREACH_MAIN_LISTBASE_ID_END; } + FOREACH_MAIN_LISTBASE_END; + + /* Delete old override IDs. */ + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT && id->newid != NULL && ID_IS_LINKED(id)) { + ID *id_override_old = BLI_ghash_lookup(linkedref_to_old_override, id); + + if (id_override_old != NULL) { + BKE_id_delete(bmain, id_override_old); + } + } + } + FOREACH_MAIN_ID_END; + + /* Essentially ensures that potentially new overrides of new objects will be instantiated. */ + lib_override_library_create_post_process(bmain, scene, view_layer, id_root_reference, id_root); /* Cleanup. */ + BLI_ghash_free(linkedref_to_old_override, NULL, NULL); + BKE_main_id_clear_newpoins(bmain); BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); return success; } +/** + * Advanced 'smart' function to delete library overrides (including their existing override + * hierarchy) and remap their usages to their linked reference IDs. + * + * \note All IDs tagged with `LIB_TAG_DOIT` will be deleted. + * + * \param id_root The root liboverride ID to resync from. + */ +void BKE_lib_override_library_delete(Main *bmain, ID *id_root) +{ + BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root)); + + /* Tag all collections and objects, as well as other IDs using them. */ + id_root->tag |= LIB_TAG_DOIT; + + /* Make a mapping 'linked reference IDs' -> 'Local override IDs' of existing overrides, and tag + * linked reference ones to be overridden again. */ + BKE_lib_override_library_override_group_tag(bmain, id_root, LIB_TAG_DOIT, true); + + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT) { + if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + ID *id_override_reference = id->override_library->reference; + + /* Remap the whole local IDs to use the linked data. */ + BKE_libblock_remap(bmain, id, id_override_reference, ID_REMAP_SKIP_INDIRECT_USAGE); + } + } + } + FOREACH_MAIN_ID_END; + + /* Delete the override IDs. */ + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (id->tag & LIB_TAG_DOIT) { + BKE_id_delete(bmain, id); + } + } + FOREACH_MAIN_ID_END; + + /* Should not actually be needed here... */ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); +} + BLI_INLINE IDOverrideLibraryRuntime *override_library_rna_path_runtime_ensure( IDOverrideLibrary *override) { @@ -1474,6 +1685,20 @@ void BKE_lib_override_library_update(Main *bmain, ID *local) /* XXX And crashing in complex cases (e.g. because depsgraph uses same data...). */ BKE_id_free_ex(bmain, tmp_id, LIB_ID_FREE_NO_UI_USER, true); + if (GS(local->name) == ID_AR) { + /* Funtime again, thanks to bone pointers in pose data of objects. We keep same ID addresses, + * but internal data has changed for sure, so we need to invalidate posebones caches. */ + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + if (ob->pose != NULL && ob->data == local) { + BLI_assert(ob->type == OB_ARMATURE); + ob->pose->flag |= POSE_RECALC; + /* We need to clear pose bone pointers immediately, some code may access those before pose + * is actually recomputed, which can lead to segfault. */ + BKE_pose_clear_pointers(ob->pose); + } + } + } + if (local->override_library->storage) { /* We know this datablock is not used anywhere besides local->override->storage. */ /* XXX For until we get fully shadow copies, we still need to ensure storage releases diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index d4246056efe..c88513ec2af 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -244,17 +244,17 @@ static void libblock_remap_data_preprocess(IDRemap *r_id_remap_data) ID *old_id = r_id_remap_data->old_id; if (!old_id || GS(old_id->name) == ID_AR) { Object *ob = (Object *)r_id_remap_data->id_owner; - /* Object's pose holds reference to armature bones... sic */ - /* Note that in theory, we should have to bother about - * linked/non-linked/never-null/etc. flags/states. + /* Object's pose holds reference to armature bones. sic */ + /* Note that in theory, we should have to bother about linked/non-linked/never-null/etc. + * flags/states. * Fortunately, this is just a tag, so we can accept to 'over-tag' a bit for pose recalc, * and avoid another complex and risky condition nightmare like the one we have in - * foreach_libblock_remap_callback()... */ + * foreach_libblock_remap_callback(). */ if (ob->pose && (!old_id || ob->data == old_id)) { BLI_assert(ob->type == OB_ARMATURE); ob->pose->flag |= POSE_RECALC; - /* We need to clear pose bone pointers immediately, things like undo writefile may be - * called before pose is actually recomputed, can lead to segfault... */ + /* We need to clear pose bone pointers immediately, some code may access those before + * pose is actually recomputed, which can lead to segfault. */ BKE_pose_clear_pointers(ob->pose); } } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 48c98be626d..4bbe3a4a26b 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -75,6 +75,12 @@ IDTypeInfo IDType_ID_LI = { .free_data = library_free_data, .make_local = NULL, .foreach_id = library_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath) diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index f42df6765c4..976fa010057 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -134,6 +134,12 @@ IDTypeInfo IDType_ID_LA = { .free_data = light_free_data, .make_local = NULL, .foreach_id = light_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; Light *BKE_light_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index f73df66b43d..b4b13306112 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -69,6 +69,12 @@ IDTypeInfo IDType_ID_LP = { .free_data = NULL, .make_local = NULL, .foreach_id = lightprobe_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void BKE_lightprobe_type_set(LightProbe *probe, const short lightprobe_type) diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index a389af5c47f..8dc44a32eaa 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -204,6 +204,12 @@ IDTypeInfo IDType_ID_LS = { .free_data = linestyle_free_data, .make_local = NULL, .foreach_id = linestyle_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static const char *modifier_name[LS_MODIFIER_NUM] = { diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index 444ed0c65b5..79b8a30242e 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -109,6 +109,12 @@ IDTypeInfo IDType_ID_MSK = { .free_data = mask_free_data, .make_local = NULL, .foreach_id = mask_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static struct { diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 0520ba3faae..d521d6c8a99 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -175,6 +175,12 @@ IDTypeInfo IDType_ID_MA = { .free_data = material_free_data, .make_local = NULL, .foreach_id = material_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void BKE_gpencil_material_attr_init(Material *ma) diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index d2f7ee68430..de477ee03b1 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -125,6 +125,12 @@ IDTypeInfo IDType_ID_MB = { .free_data = metaball_free_data, .make_local = NULL, .foreach_id = metaball_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* Functions */ diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index 2a16d0eb0f8..a7568bcd6ea 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -23,6 +23,9 @@ #include "MEM_guardedalloc.h" +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + #include "DNA_defaults.h" #include "DNA_key_types.h" #include "DNA_material_types.h" @@ -32,6 +35,7 @@ #include "BLI_bitmap.h" #include "BLI_edgehash.h" +#include "BLI_endian_switch.h" #include "BLI_ghash.h" #include "BLI_hash.h" #include "BLI_linklist.h" @@ -43,6 +47,7 @@ #include "BLT_translation.h" #include "BKE_anim_data.h" +#include "BKE_deform.h" #include "BKE_editmesh.h" #include "BKE_global.h" #include "BKE_idtype.h" @@ -63,6 +68,8 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "BLO_read_write.h" + static void mesh_clear_geometry(Mesh *mesh); static void mesh_tessface_clear_intern(Mesh *mesh, int free_customdata); @@ -163,6 +170,158 @@ static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) } } +static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address) +{ + Mesh *mesh = (Mesh *)id; + if (mesh->id.us > 0 || BLO_write_is_undo(writer)) { + /* cache only - don't write */ + mesh->mface = NULL; + mesh->totface = 0; + memset(&mesh->fdata, 0, sizeof(mesh->fdata)); + memset(&mesh->runtime, 0, sizeof(mesh->runtime)); + + BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); + BKE_id_blend_write(writer, &mesh->id); + + /* direct data */ + if (mesh->adt) { + BKE_animdata_blend_write(writer, mesh->adt); + } + + BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); + BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); + + CustomData_blend_write(writer, &mesh->vdata, mesh->totvert, CD_MASK_MESH.vmask, &mesh->id); + CustomData_blend_write(writer, &mesh->edata, mesh->totedge, CD_MASK_MESH.emask, &mesh->id); + /* fdata is really a dummy - written so slots align */ + CustomData_blend_write(writer, &mesh->fdata, mesh->totface, CD_MASK_MESH.fmask, &mesh->id); + CustomData_blend_write(writer, &mesh->ldata, mesh->totloop, CD_MASK_MESH.lmask, &mesh->id); + CustomData_blend_write(writer, &mesh->pdata, mesh->totpoly, CD_MASK_MESH.pmask, &mesh->id); + } +} + +static void mesh_blend_read_data(BlendDataReader *reader, ID *id) +{ + Mesh *mesh = (Mesh *)id; + BLO_read_pointer_array(reader, (void **)&mesh->mat); + + BLO_read_data_address(reader, &mesh->mvert); + BLO_read_data_address(reader, &mesh->medge); + BLO_read_data_address(reader, &mesh->mface); + BLO_read_data_address(reader, &mesh->mloop); + BLO_read_data_address(reader, &mesh->mpoly); + BLO_read_data_address(reader, &mesh->tface); + BLO_read_data_address(reader, &mesh->mtface); + BLO_read_data_address(reader, &mesh->mcol); + BLO_read_data_address(reader, &mesh->dvert); + BLO_read_data_address(reader, &mesh->mloopcol); + BLO_read_data_address(reader, &mesh->mloopuv); + BLO_read_data_address(reader, &mesh->mselect); + + /* animdata */ + BLO_read_data_address(reader, &mesh->adt); + BKE_animdata_blend_read_data(reader, mesh->adt); + + /* Normally BKE_defvert_blend_read should be called in CustomData_blend_read, + * but for backwards compatibility in do_versions to work we do it here. */ + BKE_defvert_blend_read(reader, mesh->totvert, mesh->dvert); + + CustomData_blend_read(reader, &mesh->vdata, mesh->totvert); + CustomData_blend_read(reader, &mesh->edata, mesh->totedge); + CustomData_blend_read(reader, &mesh->fdata, mesh->totface); + CustomData_blend_read(reader, &mesh->ldata, mesh->totloop); + CustomData_blend_read(reader, &mesh->pdata, mesh->totpoly); + + mesh->texflag &= ~ME_AUTOSPACE_EVALUATED; + mesh->edit_mesh = NULL; + BKE_mesh_runtime_reset(mesh); + + /* happens with old files */ + if (mesh->mselect == NULL) { + mesh->totselect = 0; + } + + /* Multires data */ + BLO_read_data_address(reader, &mesh->mr); + if (mesh->mr) { + BLO_read_list(reader, &mesh->mr->levels); + MultiresLevel *lvl = mesh->mr->levels.first; + + CustomData_blend_read(reader, &mesh->mr->vdata, lvl->totvert); + BKE_defvert_blend_read( + reader, lvl->totvert, CustomData_get(&mesh->mr->vdata, 0, CD_MDEFORMVERT)); + CustomData_blend_read(reader, &mesh->mr->fdata, lvl->totface); + + BLO_read_data_address(reader, &mesh->mr->edge_flags); + BLO_read_data_address(reader, &mesh->mr->edge_creases); + + BLO_read_data_address(reader, &mesh->mr->verts); + + /* If mesh has the same number of vertices as the + * highest multires level, load the current mesh verts + * into multires and discard the old data. Needed + * because some saved files either do not have a verts + * array, or the verts array contains out-of-date + * data. */ + if (mesh->totvert == ((MultiresLevel *)mesh->mr->levels.last)->totvert) { + if (mesh->mr->verts) { + MEM_freeN(mesh->mr->verts); + } + mesh->mr->verts = MEM_dupallocN(mesh->mvert); + } + + for (; lvl; lvl = lvl->next) { + BLO_read_data_address(reader, &lvl->verts); + BLO_read_data_address(reader, &lvl->faces); + BLO_read_data_address(reader, &lvl->edges); + BLO_read_data_address(reader, &lvl->colfaces); + } + } + + /* if multires is present but has no valid vertex data, + * there's no way to recover it; silently remove multires */ + if (mesh->mr && !mesh->mr->verts) { + multires_free(mesh->mr); + mesh->mr = NULL; + } + + if ((BLO_read_requires_endian_switch(reader)) && mesh->tface) { + TFace *tf = mesh->tface; + for (int i = 0; i < mesh->totface; i++, tf++) { + BLI_endian_switch_uint32_array(tf->col, 4); + } + } +} + +static void mesh_blend_read_lib(BlendLibReader *reader, ID *id) +{ + Mesh *me = (Mesh *)id; + /* this check added for python created meshes */ + if (me->mat) { + for (int i = 0; i < me->totcol; i++) { + BLO_read_id_address(reader, me->id.lib, &me->mat[i]); + } + } + else { + me->totcol = 0; + } + + BLO_read_id_address(reader, me->id.lib, &me->ipo); // XXX: deprecated: old anim sys + BLO_read_id_address(reader, me->id.lib, &me->key); + BLO_read_id_address(reader, me->id.lib, &me->texcomesh); +} + +static void mesh_read_expand(BlendExpander *expander, ID *id) +{ + Mesh *me = (Mesh *)id; + for (int a = 0; a < me->totcol; a++) { + BLO_expand(expander, me->mat[a]); + } + + BLO_expand(expander, me->key); + BLO_expand(expander, me->texcomesh); +} + IDTypeInfo IDType_ID_ME = { .id_code = ID_ME, .id_filter = FILTER_ID_ME, @@ -178,6 +337,12 @@ IDTypeInfo IDType_ID_ME = { .free_data = mesh_free_data, .make_local = NULL, .foreach_id = mesh_foreach_id, + .foreach_cache = NULL, + + .blend_write = mesh_blend_write, + .blend_read_data = mesh_blend_read_data, + .blend_read_lib = mesh_blend_read_lib, + .blend_read_expand = mesh_read_expand, }; enum { diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.c b/source/blender/blenkernel/intern/mesh_remesh_voxel.c index 05cc9769bf7..ab07e08c928 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.c +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.c @@ -48,6 +48,8 @@ #include "bmesh_tools.h" +#include "admmpd_api.h" + #ifdef WITH_OPENVDB # include "openvdb_capi.h" #endif @@ -167,11 +169,11 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh, void *update_cb, void *update_cb_data) { - /* Ensure that the triangulated mesh data is up to data */ + /* Ensure that the triangulated mesh data is up to data. */ BKE_mesh_runtime_looptri_recalc(input_mesh); const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh); - /* Gather the required data for export to the internal quadiflow mesh format */ + /* Gather the required data for export to the internal quadiflow mesh format. */ MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh), "remesh_looptri"); BKE_mesh_runtime_verttri_from_looptri( @@ -197,7 +199,7 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh, faces[i * 3 + 2] = vt->tri[2]; } - /* Fill out the required input data */ + /* Fill out the required input data. */ QuadriflowRemeshData qrd; qrd.totfaces = totfaces; @@ -215,7 +217,7 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh, qrd.out_faces = NULL; - /* Run the remesher */ + /* Run the remesher. */ QFLOW_quadriflow_remesh(&qrd, update_cb, update_cb_data); MEM_freeN(verts); @@ -223,7 +225,7 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh, MEM_freeN(verttri); if (qrd.out_faces == NULL) { - /* The remeshing was canceled */ + /* The remeshing was canceled. */ return NULL; } @@ -267,11 +269,11 @@ static Mesh *BKE_mesh_remesh_quadriflow(Mesh *input_mesh, #ifdef WITH_TETGEN static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int *numtets) { - // Ensure that the triangulated mesh data is up to data + /* Ensure that the triangulated mesh data is up to data. */ BKE_mesh_runtime_looptri_recalc(input_mesh); const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh); - // Gather the required data + /* Gather the required data. */ MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh), "remesh_looptri"); BKE_mesh_runtime_verttri_from_looptri( @@ -297,7 +299,7 @@ static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int * faces[i * 3 + 2] = vt->tri[2]; } - // Call the tetgen remesher + /* Call the tetgen remesher. */ TetGenRemeshData tg; init_tetgenremeshdata(&tg); tg.in_totfaces = totfaces; @@ -317,7 +319,7 @@ static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int * Mesh *mesh = NULL; if (success) { - // Construct the new output mesh + /* Construct the new output mesh. */ mesh = BKE_mesh_new_nomain(tg.out_totverts, 0, 0, (tg.out_totfacets * 3), tg.out_totfacets); for (int i = 0; i < tg.out_totverts; i++) { @@ -339,10 +341,9 @@ static Mesh *BKE_mesh_remesh_tetgen(Mesh *input_mesh, unsigned int **tets, int * *numtets = tg.out_tottets; *tets = (unsigned int *)MEM_malloc_arrayN( tg.out_tottets * 4, sizeof(unsigned int), "remesh_output_tets"); - //*tets = (unsigned int *)malloc(tg.out_tottets*4*sizeof(unsigned int)); memcpy(*tets, tg.out_tets, tg.out_tottets * 4 * sizeof(unsigned int)); - } // end success + } if (tg.out_verts != NULL) { MEM_freeN(tg.out_verts); @@ -375,6 +376,118 @@ struct Mesh *BKE_mesh_remesh_tetgen_to_mesh_nomain(struct Mesh *mesh, return NULL; } +static Mesh *BKE_mesh_remesh_tetlattice(struct Mesh *input_mesh, + int subdivisions, + unsigned int **tets, + int *numtets) +{ + + /* Ensure that the triangulated mesh data is up to data. */ + BKE_mesh_runtime_looptri_recalc(input_mesh); + const MLoopTri *looptri = BKE_mesh_runtime_looptri_ensure(input_mesh); + + /* Gather the required data. */ + MVertTri *verttri = MEM_callocN(sizeof(*verttri) * BKE_mesh_runtime_looptri_len(input_mesh), + "remesh_looptri"); + BKE_mesh_runtime_verttri_from_looptri( + verttri, input_mesh->mloop, looptri, BKE_mesh_runtime_looptri_len(input_mesh)); + + unsigned int totfaces = BKE_mesh_runtime_looptri_len(input_mesh); + unsigned int totverts = input_mesh->totvert; + float *verts = (float *)MEM_malloc_arrayN(totverts * 3, sizeof(float), "remesh_input_verts"); + unsigned int *faces = (unsigned int *)MEM_malloc_arrayN( + totfaces * 3, sizeof(unsigned int), "remesh_intput_faces"); + + for (unsigned int i = 0; i < totverts; i++) { + MVert *mvert = &input_mesh->mvert[i]; + verts[i * 3] = mvert->co[0]; + verts[i * 3 + 1] = mvert->co[1]; + verts[i * 3 + 2] = mvert->co[2]; + } + + for (unsigned int i = 0; i < totfaces; i++) { + MVertTri *vt = &verttri[i]; + faces[i * 3] = vt->tri[0]; + faces[i * 3 + 1] = vt->tri[1]; + faces[i * 3 + 2] = vt->tri[2]; + } + + float *out_verts = NULL; + int out_totverts = 0; + admmpd_compute_lattice( + subdivisions, + verts, totverts, + faces, totfaces, + &out_verts, &out_totverts, + tets, numtets); + bool success = out_totverts>0 && *numtets>0; + + MEM_freeN(verts); + verts = NULL; + MEM_freeN(faces); + faces = NULL; + MEM_freeN(verttri); + verttri = NULL; + + Mesh *mesh = NULL; + if (success) { + + int nt = *numtets; + int nf = *numtets * 4; + + /* Construct the new output mesh. */ + mesh = BKE_mesh_new_nomain(out_totverts, 0, 0, (nf*3), nf); + + for (int i = 0; i < out_totverts; i++) { + copy_v3_v3(mesh->mvert[i].co, &out_verts[i * 3]); + } + + MPoly *mp = mesh->mpoly; + MLoop *ml = mesh->mloop; + for (int i=0; i<nt; ++i) { + + int tet[4]; + tet[0] = (*tets)[i*4+0]; + tet[1] = (*tets)[i*4+1]; + tet[2] = (*tets)[i*4+2]; + tet[3] = (*tets)[i*4+3]; + + int tet_facets[4*3] = { + 0, 2, 1, + 0, 1, 3, + 0, 3, 2, + 1, 2, 3 + }; + + for (int j = 0; j < 4; j++, mp++, ml += 3) { + mp->loopstart = (int)(ml - mesh->mloop); + mp->totloop = 3; + ml[0].v = tet[tet_facets[j*3+0]]; + ml[1].v = tet[tet_facets[j*3+1]]; + ml[2].v = tet[tet_facets[j*3+2]]; + } + } + + } + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_calc_normals(mesh); + + if (out_verts != NULL) { + MEM_freeN(out_verts); + out_verts = NULL; + } + + return mesh; +} + +struct Mesh *BKE_mesh_remesh_tetlattice_to_mesh_nomain(struct Mesh *mesh, + int subdivisions, + unsigned int **tets, + int *numtets) +{ + return BKE_mesh_remesh_tetlattice(mesh, subdivisions, tets, numtets); +} + Mesh *BKE_mesh_remesh_quadriflow_to_mesh_nomain(Mesh *mesh, int target_faces, int seed, diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index dcac7b01899..55e43a2b8ed 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -162,6 +162,11 @@ IDTypeInfo IDType_ID_MC = { .make_local = NULL, .foreach_id = movie_clip_foreach_id, .foreach_cache = movie_clip_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /*********************** movieclip buffer loaders *************************/ diff --git a/source/blender/blenkernel/intern/multires_reshape.c b/source/blender/blenkernel/intern/multires_reshape.c index 64cc9130e25..5bcf8f62f86 100644 --- a/source/blender/blenkernel/intern/multires_reshape.c +++ b/source/blender/blenkernel/intern/multires_reshape.c @@ -189,6 +189,12 @@ void multiresModifier_subdivide_to_level(struct Object *object, } Mesh *coarse_mesh = object->data; + if (coarse_mesh->totloop == 0) { + /* If there are no loops in the mesh implies there is no CD_MDISPS as well. So can early output + * from here as there is nothing to subdivide. */ + return; + } + MultiresReshapeContext reshape_context; /* There was no multires at all, all displacement is at 0. Can simply make sure all mdisps grids diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 1ba82b352d1..0c3abc70a43 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -54,6 +54,8 @@ #include "BKE_nla.h" #include "BKE_sound.h" +#include "BLO_read_write.h" + #include "RNA_access.h" #include "nla_private.h" @@ -1361,6 +1363,25 @@ static void nlastrip_fix_resize_overlaps(NlaStrip *strip) } } +/** Recalculate the start and end frames for the strip to match the bounds of its action such that + * the overall NLA animation result is unchanged. */ +void BKE_nlastrip_recalculate_bounds_sync_action(NlaStrip *strip) +{ + float prev_actstart; + + if (strip == NULL || strip->type != NLASTRIP_TYPE_CLIP) { + return; + } + + prev_actstart = strip->actstart; + + calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); + + /* Set start such that key's do not visually move, to preserve the overall animation result. */ + strip->start += (strip->actstart - prev_actstart) * strip->scale; + + BKE_nlastrip_recalculate_bounds(strip); +} /* Recalculate the start and end frames for the current strip, after changing * the extents of the action or the mapping (repeats or scale factor) info */ @@ -2133,11 +2154,7 @@ void BKE_nla_tweakmode_exit(AnimData *adt) /* must be action-clip only (transitions don't have scale) */ if ((strip->type == NLASTRIP_TYPE_CLIP) && (strip->act)) { - /* recalculate the length of the action */ - calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); - - /* adjust the strip extents in response to this */ - BKE_nlastrip_recalculate_bounds(strip); + BKE_nlastrip_recalculate_bounds_sync_action(strip); } } @@ -2151,11 +2168,7 @@ void BKE_nla_tweakmode_exit(AnimData *adt) /* sync strip extents if this strip uses the same action */ if ((adt->actstrip) && (adt->actstrip->act == strip->act) && (strip->flag & NLASTRIP_FLAG_SYNC_LENGTH)) { - /* recalculate the length of the action */ - calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); - - /* adjust the strip extents in response to this */ - BKE_nlastrip_recalculate_bounds(strip); + BKE_nlastrip_recalculate_bounds_sync_action(strip); } /* clear tweakuser flag */ @@ -2179,3 +2192,103 @@ void BKE_nla_tweakmode_exit(AnimData *adt) adt->actstrip = NULL; adt->flag &= ~ADT_NLA_EDIT_ON; } + +static void blend_write_nla_strips(BlendWriter *writer, ListBase *strips) +{ + BLO_write_struct_list(writer, NlaStrip, strips); + LISTBASE_FOREACH (NlaStrip *, strip, strips) { + /* write the strip's F-Curves and modifiers */ + BKE_fcurve_blend_write(writer, &strip->fcurves); + BKE_fmodifiers_blend_write(writer, &strip->modifiers); + + /* write the strip's children */ + blend_write_nla_strips(writer, &strip->strips); + } +} + +static void blend_data_read_nla_strips(BlendDataReader *reader, ListBase *strips) +{ + LISTBASE_FOREACH (NlaStrip *, strip, strips) { + /* strip's child strips */ + BLO_read_list(reader, &strip->strips); + blend_data_read_nla_strips(reader, &strip->strips); + + /* strip's F-Curves */ + BLO_read_list(reader, &strip->fcurves); + BKE_fcurve_blend_read_data(reader, &strip->fcurves); + + /* strip's F-Modifiers */ + BLO_read_list(reader, &strip->modifiers); + BKE_fmodifiers_blend_read_data(reader, &strip->modifiers, NULL); + } +} + +static void blend_lib_read_nla_strips(BlendLibReader *reader, ID *id, ListBase *strips) +{ + LISTBASE_FOREACH (NlaStrip *, strip, strips) { + /* check strip's children */ + blend_lib_read_nla_strips(reader, id, &strip->strips); + + /* check strip's F-Curves */ + BKE_fcurve_blend_read_lib(reader, id, &strip->fcurves); + + /* reassign the counted-reference to action */ + BLO_read_id_address(reader, id->lib, &strip->act); + } +} + +static void blend_read_expand_nla_strips(BlendExpander *expander, ListBase *strips) +{ + LISTBASE_FOREACH (NlaStrip *, strip, strips) { + /* check child strips */ + blend_read_expand_nla_strips(expander, &strip->strips); + + /* check F-Curves */ + BKE_fcurve_blend_read_expand(expander, &strip->fcurves); + + /* check F-Modifiers */ + BKE_fmodifiers_blend_read_expand(expander, &strip->modifiers); + + /* relink referenced action */ + BLO_expand(expander, strip->act); + } +} + +void BKE_nla_blend_write(BlendWriter *writer, ListBase *tracks) +{ + /* write all the tracks */ + LISTBASE_FOREACH (NlaTrack *, nlt, tracks) { + /* write the track first */ + BLO_write_struct(writer, NlaTrack, nlt); + + /* write the track's strips */ + blend_write_nla_strips(writer, &nlt->strips); + } +} + +void BKE_nla_blend_read_data(BlendDataReader *reader, ListBase *tracks) +{ + LISTBASE_FOREACH (NlaTrack *, nlt, tracks) { + /* relink list of strips */ + BLO_read_list(reader, &nlt->strips); + + /* relink strip data */ + blend_data_read_nla_strips(reader, &nlt->strips); + } +} + +void BKE_nla_blend_read_lib(BlendLibReader *reader, ID *id, ListBase *tracks) +{ + /* we only care about the NLA strips inside the tracks */ + LISTBASE_FOREACH (NlaTrack *, nlt, tracks) { + blend_lib_read_nla_strips(reader, id, &nlt->strips); + } +} + +void BKE_nla_blend_read_expand(struct BlendExpander *expander, struct ListBase *tracks) +{ + /* nla-data - referenced actions */ + LISTBASE_FOREACH (NlaTrack *, nlt, tracks) { + blend_read_expand_nla_strips(expander, &nlt->strips); + } +} diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index a89285a28c1..571a1145958 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -359,6 +359,11 @@ IDTypeInfo IDType_ID_NT = { .make_local = NULL, .foreach_id = node_foreach_id, .foreach_cache = node_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 15ad653e6f8..23f28e4ea23 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -156,7 +156,6 @@ static ThreadMutex vparent_lock = BLI_MUTEX_INITIALIZER; #endif static void copy_object_pose(Object *obn, const Object *ob, const int flag); -static void copy_object_lod(Object *obn, const Object *ob, const int flag); static void object_init_data(ID *id) { @@ -264,8 +263,6 @@ static void object_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const in ob_dst->avs = ob_src->avs; ob_dst->mpath = animviz_copy_motionpath(ob_src->mpath); - copy_object_lod(ob_dst, ob_src, flag_subdata); - /* Do not copy object's preview * (mostly due to the fact renderers create temp copy of objects). */ if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) { /* XXX TODO temp hack */ @@ -314,8 +311,6 @@ static void object_free_data(ID *id) BLI_freelistN(&ob->pc_ids); - BLI_freelistN(&ob->lodlevels); - /* Free runtime curves data. */ if (ob->runtime.curve_cache) { BKE_curve_bevelList_free(&ob->runtime.curve_cache->bev); @@ -499,12 +494,6 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); } - if (object->lodlevels.first) { - LISTBASE_FOREACH (LodLevel *, level, &object->lodlevels) { - BKE_LIB_FOREACHID_PROCESS(data, level->source, IDWALK_CB_NEVER_SELF); - } - } - BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data); BKE_gpencil_modifiers_foreach_ID_link( object, library_foreach_gpencil_modifiersForeachIDLink, data); @@ -539,6 +528,12 @@ IDTypeInfo IDType_ID_OB = { .free_data = object_free_data, .make_local = object_make_local, .foreach_id = object_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void BKE_object_workob_clear(Object *workob) @@ -1395,7 +1390,6 @@ Object *BKE_object_add_for_data( void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src, const int flag) { - SoftBody *sb = ob_src->soft; SoftBody *sbn; bool tagged_no_main = ob_dst->id.tag & LIB_TAG_NO_MAIN; @@ -1408,6 +1402,8 @@ void BKE_object_copy_softbody(struct Object *ob_dst, const struct Object *ob_src sbn = MEM_dupallocN(sb); + sbExternalCopy(ob_dst,ob_src); + if ((flag & LIB_ID_COPY_CACHES) == 0) { sbn->totspring = sbn->totpoint = 0; sbn->bpoint = NULL; @@ -1586,13 +1582,6 @@ static void copy_object_pose(Object *obn, const Object *ob, const int flag) } } -static void copy_object_lod(Object *obn, const Object *ob, const int UNUSED(flag)) -{ - BLI_duplicatelist(&obn->lodlevels, &ob->lodlevels); - - obn->currentlod = (LodLevel *)obn->lodlevels.first; -} - bool BKE_object_pose_context_check(const Object *ob) { if ((ob) && (ob->type == OB_ARMATURE) && (ob->pose) && (ob->mode & OB_MODE_POSE)) { diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c index e3c209b60e6..bc089d7bd80 100644 --- a/source/blender/blenkernel/intern/paint.c +++ b/source/blender/blenkernel/intern/paint.c @@ -117,6 +117,12 @@ IDTypeInfo IDType_ID_PAL = { .free_data = palette_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static void paint_curve_copy_data(Main *UNUSED(bmain), @@ -155,6 +161,12 @@ IDTypeInfo IDType_ID_PC = { .free_data = paint_curve_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; const char PAINT_CURSOR_SCULPT[3] = {255, 100, 100}; diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index b3da6c53b34..e837c57400a 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -209,6 +209,12 @@ IDTypeInfo IDType_ID_PA = { .free_data = particle_settings_free_data, .make_local = NULL, .foreach_id = particle_settings_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; unsigned int PSYS_FRAND_SEED_OFFSET[PSYS_FRAND_COUNT]; diff --git a/source/blender/blenkernel/intern/pointcloud.c b/source/blender/blenkernel/intern/pointcloud.c index 21889acba3c..fb10c9f03e3 100644 --- a/source/blender/blenkernel/intern/pointcloud.c +++ b/source/blender/blenkernel/intern/pointcloud.c @@ -113,6 +113,12 @@ IDTypeInfo IDType_ID_PT = { .free_data = pointcloud_free_data, .make_local = NULL, .foreach_id = pointcloud_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; static void pointcloud_random(PointCloud *pointcloud) diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index ece7d0f9136..95a8b3b3c15 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -229,6 +229,23 @@ void BKE_rigidbody_free_constraint(Object *ob) ob->rigidbody_constraint = NULL; } +bool BKE_rigidbody_is_affected_by_simulation(Object *ob) +{ + /* Check if the object will have its transform changed by the rigidbody simulation. */ + + /* True if the shape of this object's parent is of type compound */ + bool obCompoundParent = (ob->parent != NULL && ob->parent->rigidbody_object != NULL && + ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND); + + RigidBodyOb *rbo = ob->rigidbody_object; + if (rbo == NULL || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE || + obCompoundParent) { + return false; + } + + return true; +} + #ifdef WITH_BULLET /* Copying Methods --------------------- */ @@ -1904,18 +1921,13 @@ bool BKE_rigidbody_check_sim_running(RigidBodyWorld *rbw, float ctime) /* Sync rigid body and object transformations */ void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) { - RigidBodyOb *rbo = ob->rigidbody_object; - - /* True if the shape of this object's parent is of type compound */ - bool obCompoundParent = (ob->parent != NULL && ob->parent->rigidbody_object != NULL && - ob->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND); - - /* keep original transform for kinematic and passive objects */ - if (ELEM(NULL, rbw, rbo) || rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE || - obCompoundParent) { + if (!BKE_rigidbody_is_affected_by_simulation(ob)) { + /* Don't sync transforms for objects that are not affected/changed by the simulation. */ return; } + RigidBodyOb *rbo = ob->rigidbody_object; + /* use rigid body transform after cache start frame if objects is not being transformed */ if (BKE_rigidbody_check_sim_running(rbw, ctime) && !(ob->base_flag & BASE_SELECTED && G.moving & G_TRANSFORM_OBJ)) { @@ -1941,8 +1953,8 @@ void BKE_rigidbody_sync_transforms(RigidBodyWorld *rbw, Object *ob, float ctime) void BKE_rigidbody_aftertrans_update( Object *ob, float loc[3], float rot[3], float quat[4], float rotAxis[3], float rotAngle) { + bool correct_delta = BKE_rigidbody_is_affected_by_simulation(ob); RigidBodyOb *rbo = ob->rigidbody_object; - bool correct_delta = !(rbo->flag & RBO_FLAG_KINEMATIC || rbo->type == RBO_TYPE_PASSIVE); /* return rigid body and object to their initial states */ copy_v3_v3(rbo->pos, ob->loc); diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 1dc51c9ddae..e74fcbb84bd 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -469,7 +469,7 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) } if (scene->ed) { Sequence *seq; - SEQP_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { BKE_LIB_FOREACHID_PROCESS(data, seq->scene, IDWALK_CB_NEVER_SELF); BKE_LIB_FOREACHID_PROCESS(data, seq->scene_camera, IDWALK_CB_NOP); BKE_LIB_FOREACHID_PROCESS(data, seq->clip, IDWALK_CB_USER); @@ -486,7 +486,7 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) BKE_LIB_FOREACHID_PROCESS(data, text_data->text_font, IDWALK_CB_USER); } } - SEQ_END; + SEQ_ALL_END; } /* This pointer can be NULL during old files reading, better be safe than sorry. */ @@ -606,6 +606,11 @@ IDTypeInfo IDType_ID_SCE = { .make_local = NULL, .foreach_id = scene_foreach_id, .foreach_cache = scene_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; const char *RE_engine_id_BLENDER_EEVEE = "BLENDER_EEVEE"; @@ -1129,15 +1134,9 @@ int BKE_scene_base_iter_next( return iter->phase; } -Scene *BKE_scene_find_from_view_layer(const Main *bmain, const ViewLayer *layer) +bool BKE_scene_has_view_layer(const Scene *scene, const ViewLayer *layer) { - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (BLI_findindex(&scene->view_layers, layer) != -1) { - return scene; - } - } - - return NULL; + return BLI_findindex(&scene->view_layers, layer) != -1; } Scene *BKE_scene_find_from_collection(const Main *bmain, const Collection *collection) @@ -1615,7 +1614,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph) */ void BKE_scene_view_layer_graph_evaluated_ensure(Main *bmain, Scene *scene, ViewLayer *view_layer) { - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); DEG_make_active(depsgraph); BKE_scene_graph_update_tagged(depsgraph, bmain); } @@ -2249,15 +2248,15 @@ void BKE_scene_free_view_layer_depsgraph(Scene *scene, ViewLayer *view_layer) /* Query depsgraph for a specific contexts. */ -static Depsgraph **scene_get_depsgraph_p(Main *bmain, - Scene *scene, +static Depsgraph **scene_get_depsgraph_p(Scene *scene, ViewLayer *view_layer, - const bool allocate_ghash_entry, - const bool allocate_depsgraph) + const bool allocate_ghash_entry) { + /* bmain may be NULL here! */ BLI_assert(scene != NULL); BLI_assert(view_layer != NULL); - BLI_assert(BKE_scene_find_from_view_layer(bmain, view_layer) == scene); + BLI_assert(BKE_scene_has_view_layer(scene, view_layer)); + /* Make sure hash itself exists. */ if (allocate_ghash_entry) { BKE_scene_ensure_depsgraph_hash(scene); @@ -2265,42 +2264,68 @@ static Depsgraph **scene_get_depsgraph_p(Main *bmain, if (scene->depsgraph_hash == NULL) { return NULL; } - /* Either ensure item is in the hash or simply return NULL if it's not, - * depending on whether caller wants us to create depsgraph or not. - */ + DepsgraphKey key; key.view_layer = view_layer; + Depsgraph **depsgraph_ptr; - if (allocate_ghash_entry) { - DepsgraphKey **key_ptr; - if (!BLI_ghash_ensure_p_ex( - scene->depsgraph_hash, &key, (void ***)&key_ptr, (void ***)&depsgraph_ptr)) { - *key_ptr = MEM_mallocN(sizeof(DepsgraphKey), __func__); - **key_ptr = key; - if (allocate_depsgraph) { - *depsgraph_ptr = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT); - /* TODO(sergey): Would be cool to avoid string format print, - * but is a bit tricky because we can't know in advance whether - * we will ever enable debug messages for this depsgraph. - */ - char name[1024]; - BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name); - DEG_debug_name_set(*depsgraph_ptr, name); - } - else { - *depsgraph_ptr = NULL; - } - } - } - else { + if (!allocate_ghash_entry) { depsgraph_ptr = (Depsgraph **)BLI_ghash_lookup_p(scene->depsgraph_hash, &key); + return depsgraph_ptr; + } + + DepsgraphKey **key_ptr; + if (BLI_ghash_ensure_p_ex( + scene->depsgraph_hash, &key, (void ***)&key_ptr, (void ***)&depsgraph_ptr)) { + return depsgraph_ptr; } + + /* Depsgraph was not found in the ghash, but the key still needs allocating. */ + *key_ptr = MEM_mallocN(sizeof(DepsgraphKey), __func__); + **key_ptr = key; + + *depsgraph_ptr = NULL; + return depsgraph_ptr; +} + +static Depsgraph **scene_ensure_depsgraph_p(Main *bmain, Scene *scene, ViewLayer *view_layer) +{ + BLI_assert(bmain != NULL); + + Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(scene, view_layer, true); + if (depsgraph_ptr == NULL) { + /* The scene has no depsgraph hash. */ + return NULL; + } + if (*depsgraph_ptr != NULL) { + /* The depsgraph was found, no need to allocate. */ + return depsgraph_ptr; + } + + /* Allocate a new depsgraph. scene_get_depsgraph_p() already ensured that the pointer is stored + * in the scene's depsgraph hash. */ + *depsgraph_ptr = DEG_graph_new(bmain, scene, view_layer, DAG_EVAL_VIEWPORT); + + /* TODO(sergey): Would be cool to avoid string format print, + * but is a bit tricky because we can't know in advance whether + * we will ever enable debug messages for this depsgraph. + */ + char name[1024]; + BLI_snprintf(name, sizeof(name), "%s :: %s", scene->id.name, view_layer->name); + DEG_debug_name_set(*depsgraph_ptr, name); + return depsgraph_ptr; } -Depsgraph *BKE_scene_get_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, bool allocate) +Depsgraph *BKE_scene_get_depsgraph(Scene *scene, ViewLayer *view_layer) +{ + Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(scene, view_layer, false); + return (depsgraph_ptr != NULL) ? *depsgraph_ptr : NULL; +} + +Depsgraph *BKE_scene_ensure_depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer) { - Depsgraph **depsgraph_ptr = scene_get_depsgraph_p(bmain, scene, view_layer, allocate, allocate); + Depsgraph **depsgraph_ptr = scene_ensure_depsgraph_p(bmain, scene, view_layer); return (depsgraph_ptr != NULL) ? *depsgraph_ptr : NULL; } @@ -2366,8 +2391,7 @@ void BKE_scene_undo_depsgraphs_restore(Main *bmain, GHash *depsgraph_extract) } BLI_assert(*depsgraph_extract_ptr != NULL); - Depsgraph **depsgraph_scene_ptr = scene_get_depsgraph_p( - bmain, scene, view_layer, true, false); + Depsgraph **depsgraph_scene_ptr = scene_get_depsgraph_p(scene, view_layer, true); BLI_assert(depsgraph_scene_ptr != NULL); BLI_assert(*depsgraph_scene_ptr == NULL); @@ -2551,13 +2575,13 @@ static void scene_sequencer_disable_sound_strips(Scene *scene) return; } Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->scene_sound != NULL) { BKE_sound_remove_scene_sound(scene, seq->scene_sound); seq->scene_sound = NULL; } } - SEQ_END; + SEQ_ALL_END; } void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene) @@ -2568,7 +2592,7 @@ void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene) } BKE_sound_ensure_scene(scene); Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->scene_sound == NULL) { if (seq->sound != NULL) { if (seq->scene_sound == NULL) { @@ -2606,7 +2630,7 @@ void BKE_scene_eval_sequencer_sequences(Depsgraph *depsgraph, Scene *scene) seq->scene_sound, seq->pan, (seq->flag & SEQ_AUDIO_PAN_ANIMATED) != 0); } } - SEQ_END; + SEQ_ALL_END; BKE_sequencer_update_muting(scene->ed); BKE_sequencer_update_sound_bounds_all(scene); } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 3a49c180172..d56658a6709 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -245,6 +245,12 @@ IDTypeInfo IDType_ID_SCR = { .free_data = screen_free_data, .make_local = NULL, .foreach_id = screen_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* ************ Spacetype/regiontype handling ************** */ diff --git a/source/blender/blenkernel/intern/seqeffects.c b/source/blender/blenkernel/intern/seqeffects.c index c6daecbcee6..aba9b255f40 100644 --- a/source/blender/blenkernel/intern/seqeffects.c +++ b/source/blender/blenkernel/intern/seqeffects.c @@ -2437,16 +2437,16 @@ static void transform_image(int x, } static void do_transform_effect(const SeqRenderData *context, - Sequence *seq, - float UNUSED(cfra), - float UNUSED(facf0), - float UNUSED(facf1), - ImBuf *ibuf1, - ImBuf *UNUSED(ibuf2), - ImBuf *UNUSED(ibuf3), - int start_line, - int total_lines, - ImBuf *out) + Sequence *seq, + float UNUSED(cfra), + float UNUSED(facf0), + float UNUSED(facf1), + ImBuf *ibuf1, + ImBuf *UNUSED(ibuf2), + ImBuf *UNUSED(ibuf3), + int start_line, + int total_lines, + ImBuf *out) { Scene *scene = context->scene; TransformVars *transform = (TransformVars *)seq->effectdata; diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index b4da0c5bd33..630c5f9fb1f 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -507,11 +507,11 @@ void BKE_sequencer_editing_free(Scene *scene, const bool do_id_user) BKE_sequencer_prefetch_free(scene); BKE_sequencer_cache_destruct(scene); - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { /* handle cache freeing above */ BKE_sequence_free_ex(scene, seq, false, do_id_user, false); } - SEQ_END; + SEQ_ALL_END; BLI_freelistN(&ed->metastack); MEM_freeN(ed); @@ -662,7 +662,7 @@ void BKE_sequencer_new_render_data(Main *bmain, /* ************************* iterator ************************** */ /* *************** (replaces old WHILE_SEQ) ********************* */ -/* **************** use now SEQ_BEGIN () SEQ_END ***************** */ +/* **************** use now SEQ_ALL_BEGIN () SEQ_ALL_END ***************** */ /* sequence strip iterator: * - builds a full array, recursively into meta strips @@ -697,7 +697,10 @@ static void seq_build_array(ListBase *seqbase, Sequence ***array, int depth) } } -static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_pointer) +static void seq_array(Editing *ed, + Sequence ***seqarray, + int *tot, + const bool use_current_sequences) { Sequence **array; @@ -708,7 +711,7 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin return; } - if (use_pointer) { + if (use_current_sequences) { seq_count(ed->seqbasep, tot); } else { @@ -720,7 +723,7 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin } *seqarray = array = MEM_mallocN(sizeof(Sequence *) * (*tot), "SeqArray"); - if (use_pointer) { + if (use_current_sequences) { seq_build_array(ed->seqbasep, &array, 0); } else { @@ -728,10 +731,10 @@ static void seq_array(Editing *ed, Sequence ***seqarray, int *tot, bool use_poin } } -void BKE_sequence_iterator_begin(Editing *ed, SeqIterator *iter, bool use_pointer) +void BKE_sequence_iterator_begin(Editing *ed, SeqIterator *iter, const bool use_current_sequences) { memset(iter, 0, sizeof(*iter)); - seq_array(ed, &iter->array, &iter->tot, use_pointer); + seq_array(ed, &iter->array, &iter->tot, use_current_sequences); if (iter->tot) { iter->cur = 0; @@ -3592,7 +3595,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, } /* opengl offscreen render */ - depsgraph = BKE_scene_get_depsgraph(context->bmain, scene, view_layer, true); + depsgraph = BKE_scene_ensure_depsgraph(context->bmain, scene, view_layer); BKE_scene_graph_update_for_newframe(depsgraph); ibuf = sequencer_view3d_fn( /* set for OpenGL render (NULL when scrubbing) */ @@ -6168,7 +6171,7 @@ void BKE_sequencer_check_uuids_unique_and_report(const Scene *scene) BLI_session_uuid_ghash_hash, BLI_session_uuid_ghash_compare, "sequencer used uuids"); const Sequence *sequence; - SEQ_BEGIN (scene->ed, sequence) { + SEQ_ALL_BEGIN (scene->ed, sequence) { const SessionUUID *session_uuid = &sequence->runtime.session_uuid; if (!BLI_session_uuid_is_generated(session_uuid)) { printf("Sequence %s does not have UUID generated.\n", sequence->name); @@ -6182,7 +6185,7 @@ void BKE_sequencer_check_uuids_unique_and_report(const Scene *scene) BLI_gset_insert(used_uuids, (void *)session_uuid); } - SEQ_END; + SEQ_ALL_END; BLI_gset_free(used_uuids, NULL); } diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 6b03721cab8..9dc1f073e2a 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -160,6 +160,12 @@ IDTypeInfo IDType_ID_SIM = { /* free_data */ simulation_free_data, /* make_local */ nullptr, /* foreach_id */ simulation_foreach_id, + /* foreach_cache */ NULL, + + /* blend_write */ NULL, + /* blend_read_data */ NULL, + /* blend_read_lib */ NULL, + /* blend_read_expand */ NULL, }; void *BKE_simulation_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index bca2af1fb18..5743394c079 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -3085,8 +3085,6 @@ static void softbody_to_object(Object *ob, float (*vertexCos)[3], int numVerts, { SoftBody *sb = ob->soft; if (sb) { - // int sb_totpt = sb->totpoint; - BodyPoint *bp = sb->bpoint; int a; if (sb->solverflags & SBSO_ESTIMATEIPO) { @@ -3128,28 +3126,6 @@ SoftBody *sbNew(Scene *scene) SoftBody *sb; sb = MEM_callocN(sizeof(SoftBody), "softbody"); - sb->solver_mode = SOLVER_MODE_LEGACY; // SOLVER_MODE_ADMMPD; - sb->admmpd_mesh_mode = 0; // embedded - sb->admmpd_substeps = 1; - sb->admmpd_max_admm_iters = 20; - sb->admmpd_self_collision = 0; - sb->admmpd_material = 0; // ARAP - sb->admmpd_embed_res = 3; - sb->admmpd_converge_eps = 1e-4; - sb->admmpd_youngs_exp = 6; - sb->admmpd_poisson = 0.399; - sb->admmpd_density_kgm3 = 1522; - sb->admmpd_ck_exp = 7; - sb->admmpd_pk_exp = 4; - sb->admmpd_floor_z = -999; - sb->admmpd_gravity = -9.8; - sb->admmpd_strainlimit_min = 0; // no compression - sb->admmpd_strainlimit_max = 100; // 100x stretch - sb->admmpd_maxthreads = -1; - sb->admmpd_loglevel = 1; // low - sb->admmpd_linsolver = 1; // PCG - sb->admmpd = MEM_callocN(sizeof(ADMMPDInterfaceData), "SoftBody_admmpd"); - sb->mediafrict = 0.5f; sb->nodemass = 1.0f; sb->grav = 9.8f; @@ -3190,6 +3166,8 @@ SoftBody *sbNew(Scene *scene) sb->shared = MEM_callocN(sizeof(*sb->shared), "SoftBody_Shared"); sb->shared->pointcache = BKE_ptcache_add(&sb->shared->ptcaches); + sbExternalSetDefault(sb); + if (!sb->effector_weights) { sb->effector_weights = BKE_effector_add_weights(NULL); } @@ -3209,15 +3187,34 @@ void sbFree(Object *ob) free_softbody_intern(sb); + /* Only free shared data on non-CoW copies */ if ((ob->id.tag & LIB_TAG_NO_MAIN) == 0) { - if (sb->admmpd) { - admmpd_dealloc(sb->admmpd); - MEM_freeN(sb->admmpd); - sb->admmpd = NULL; + /* Clear the ADMMPD linked list. Because the linked + * list pointer is copied to all soft body objects, we + * want to be careful not to free twice. */ + { + ADMMPDInterfaceData *admmpd; + int cleared_admmpd_list = 0; + if (sb->shared->admmpd_list) { + while ((admmpd = BLI_pophead(sb->shared->admmpd_list))) { + cleared_admmpd_list = 1; + if (admmpd != NULL) { + admmpd_dealloc(admmpd); + MEM_freeN(admmpd); + } + } + } + /* If we deleted data from the linked list + * we can delete the head ptr */ + if (cleared_admmpd_list) { + MEM_freeN(sb->shared->admmpd_list); + } + /* Otherwise we've allready freed, just + * set the ptr to NULL */ + sb->shared->admmpd_list = NULL; } - /* Only free shared data on non-CoW copies */ BKE_ptcache_free_list(&sb->shared->ptcaches); sb->shared->pointcache = NULL; MEM_freeN(sb->shared); @@ -3564,15 +3561,40 @@ static void sbStoreLastFrame(struct Depsgraph *depsgraph, Object *object, float object_orig->soft->last_frame = framenr; } +static inline ADMMPDInterfaceData* get_admmpd_for_object(Object *ob) +{ + SoftBody *sb = ob->soft; + if (!sb) { + return NULL; + } + int found = 0; + ADMMPDInterfaceData *admmpd = sb->shared->admmpd_list->first; + while (admmpd != NULL) { + if (strcmp(admmpd->name,ob->id.name)==0) { + found = 1; + break; + } + admmpd = admmpd->next; + } + if (!found) { + return NULL; + } + return admmpd; +} + static void update_collider_admmpd(Depsgraph *depsgraph, Collection *collection, Object *vertexowner) { + SoftBody *sb = vertexowner->soft; if (!sb) { return; } - if (!sb->admmpd) { + + ADMMPDInterfaceData *admmpd = get_admmpd_for_object(vertexowner); + if (!admmpd) { + CLOG_ERROR(&LOG, "No ADMM-PD data found for object %s", vertexowner->id.name); return; } @@ -3580,20 +3602,32 @@ static void update_collider_admmpd(Depsgraph *depsgraph, Object **objects = BKE_collision_objects_create( depsgraph, vertexowner, collection, &numobjects, eModifierType_Collision); - // How many faces and vertices do we need to allocate? + admmpd_update_obstacles(admmpd,objects,numobjects); + + BKE_collision_objects_free(objects); + +#if 0 + /* How many faces and vertices do we need to allocate? */ int tot_verts = 0; int tot_faces = 0; for (int i = 0; i < numobjects; ++i) { Object *ob = objects[i]; - if (ob->type == OB_MESH) { - if (ob->pd && ob->pd->deflect) { - CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( - ob, eModifierType_Collision); - if (!cmd) - continue; - tot_verts += cmd->mvert_num; - tot_faces += cmd->tri_num; - } + if (ob->type != OB_MESH) { + continue; + } + /* If the collision object is the same as the softbody object, + * we don't want to add it. Otherwise it creates a duplicate obstacle + * at the initial location of the softbody. */ + if (strcmp(ob->id.name,vertexowner->id.name)==0) { + continue; + } + if (ob->pd && ob->pd->deflect) { + CollisionModifierData *cmd = (CollisionModifierData *)BKE_modifiers_findby_type( + ob, eModifierType_Collision); + if (!cmd) + continue; + tot_verts += cmd->mvert_num; + tot_faces += cmd->tri_num; } } @@ -3601,7 +3635,7 @@ static void update_collider_admmpd(Depsgraph *depsgraph, float *obs_v1 = MEM_callocN(sizeof(float) * 3 * tot_verts, __func__); unsigned int *obs_faces = MEM_callocN(sizeof(unsigned int) * 3 * tot_faces, __func__); - // Copy over vertices and faces + /* Copy over vertices and faces */ int curr_verts = 0; int curr_faces = 0; for (int i = 0; i < numobjects; ++i) { @@ -3634,26 +3668,77 @@ static void update_collider_admmpd(Depsgraph *depsgraph, } } - // Update via API - admmpd_update_obstacles(sb->admmpd, obs_v0, obs_v1, tot_verts, obs_faces, tot_faces); + /* Pass obstacle data to ADMMPD */ + admmpd_update_obstacles(admmpd, obs_v0, obs_v1, tot_verts, obs_faces, tot_faces); - // Cleanup MEM_freeN(obs_v0); MEM_freeN(obs_v1); MEM_freeN(obs_faces); BKE_collision_objects_free(objects); +#endif } -void sbCustomRead(struct Object *ob) +void sbExternalCopy(Object *dest_ob, Object *src_ob) +{ + (void)(src_ob); + if (!dest_ob) { + CLOG_ERROR(&LOG, "No dest object in sbExternalCopy"); + return; + } + SoftBody *dest = dest_ob->soft; + if (!dest) { + CLOG_ERROR(&LOG, "No softbody in sbExternalCopy"); + return; + } + if (!dest->shared) { + CLOG_ERROR(&LOG, "No shared in sbExternalCopy"); + return; + } + /* This can happen on readfile: */ + if (!dest->shared->admmpd_list) { + dest->shared->admmpd_list = MEM_callocN(sizeof(ListBase), "SoftBody_ADMMPD_List"); + } + /* Create ADMM-PD data for this object if it does not already exist. */ + ADMMPDInterfaceData *admmpd = get_admmpd_for_object(dest_ob); + if (admmpd==NULL) { + admmpd = MEM_callocN(sizeof(ADMMPDInterfaceData), "ADMMPD"); + strcpy(admmpd->name, dest_ob->id.name); + BLI_addtail(dest->shared->admmpd_list, admmpd); + } +} + +void sbExternalSetDefault(SoftBody *sb) { - SoftBody *sb = ob->soft; if (!sb) { return; } + if (sb->shared == NULL) { + sb->shared = MEM_callocN(sizeof(*sb->shared), "SoftBody_Shared"); + } + if (sb->shared->admmpd_list == NULL) { + sb->shared->admmpd_list = MEM_callocN(sizeof(ListBase), "SoftBody_ADMMPD_List"); + } - // ADMM-PD data is not currently written to file. - // Instead, we'll create new data when a .blend file is loaded. - sb->admmpd = MEM_callocN(sizeof(ADMMPDInterfaceData), "SoftBody_admmpd"); + sb->solver_mode = SOLVER_MODE_LEGACY; + sb->admmpd_mesh_mode = 0; /* Embedded. */ + sb->admmpd_substeps = 1; + sb->admmpd_max_admm_iters = 20; + sb->admmpd_self_collision = 0; + sb->admmpd_material = 0; /* ARAP. */ + sb->admmpd_embed_res = 3; /* Max subdivisions for embedded lattice. */ + sb->admmpd_converge_eps = 1e-4; /* Solver residual stop condition. */ + sb->admmpd_youngs_exp = 6; /* Exponent of Young's mod. */ + sb->admmpd_poisson = 0.399; + sb->admmpd_density_kgm3 = 1522; /* Density of rubber. */ + sb->admmpd_ck_exp = 7; /* Exponent of collision stiffness. */ + sb->admmpd_pk_exp = 4; /* Exponent of pin stiffness. */ + sb->admmpd_floor_z = -999; + sb->admmpd_gravity = -9.8; + sb->admmpd_strainlimit_min = 0; /* 0 = compression allowed. */ + sb->admmpd_strainlimit_max = 100; /* 100 = 100x strech allowed. */ + sb->admmpd_maxthreads = -1; /* Auto detect. */ + sb->admmpd_loglevel = 1; /* Low terminal output. */ + sb->admmpd_linsolver = 1; /* PCG solver. */ } /* simulates one step. framenr is in frames */ @@ -3670,9 +3755,10 @@ void sbObjectStep(struct Depsgraph *depsgraph, return; } - if (sb->admmpd == NULL) { - CLOG_ERROR(&LOG, "No ADMM-PD data"); - WM_reportf(RPT_ERROR, "No ADMM-PD data"); + ADMMPDInterfaceData *admmpd = get_admmpd_for_object(ob); + if (admmpd == NULL) { + CLOG_ERROR(&LOG, "No ADMM-PD data in sbObjectStep"); + WM_report(RPT_ERROR, "No ADMM-PD data in sbObjectStep"); return; } @@ -3714,18 +3800,17 @@ void sbObjectStep(struct Depsgraph *depsgraph, /* Do we need to initialize the ADMM-PD mesh? * a) Has never been initialized. - * b) The mesh topology has changed. - * TODO: ob->obmat or vertexCos change. */ + * b) The mesh topology has changed. */ int init_mesh = 0; - if (is_first_frame || admmpd_mesh_needs_update(sb->admmpd, ob)) { - init_mesh = admmpd_update_mesh(sb->admmpd, ob, vertexCos); + if (is_first_frame || admmpd_mesh_needs_update(admmpd, ob)) { + init_mesh = admmpd_update_mesh(admmpd, ob, vertexCos); if (init_mesh == 0) { - CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error); - WM_reportf(RPT_ERROR, sb->admmpd->last_error); + CLOG_ERROR(&LOG, "%s", admmpd->last_error); + WM_report(RPT_ERROR, admmpd->last_error); return; } else if (init_mesh == -1) { - WM_reportf(RPT_WARNING, sb->admmpd->last_error); + WM_report(RPT_WARNING, admmpd->last_error); } } @@ -3734,22 +3819,22 @@ void sbObjectStep(struct Depsgraph *depsgraph, * b) Some settings require re-initialization * c) The mesh has changed */ int init_solver = 0; - if (is_first_frame || init_mesh || admmpd_solver_needs_update(sb->admmpd, scene, ob)) { - init_solver = admmpd_update_solver(sb->admmpd, scene, ob, vertexCos); + if (is_first_frame || init_mesh || admmpd_solver_needs_update(admmpd, scene, ob)) { + init_solver = admmpd_update_solver(admmpd, scene, ob, vertexCos); if (init_solver == 0) { - CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error); - WM_reportf(RPT_ERROR, sb->admmpd->last_error); + CLOG_ERROR(&LOG, "%s", admmpd->last_error); + WM_report(RPT_ERROR, admmpd->last_error); return; } else if (init_solver == -1) { - WM_reportf(RPT_WARNING, sb->admmpd->last_error); + WM_report(RPT_WARNING, admmpd->last_error); } } /* In case of paramter change, ob->soft->bpoint has not * been set yet. So we need to resize which can be done * in the copy_to_object function while leaving vertexCos to null. */ - admmpd_copy_to_object(sb->admmpd, ob, NULL); + admmpd_copy_to_object(admmpd, ob, NULL); if (init_mesh || init_solver) { BKE_ptcache_invalidate(cache); @@ -3790,7 +3875,7 @@ void sbObjectStep(struct Depsgraph *depsgraph, /* first frame, no simulation to do, just set the positions */ if (sb->solver_mode == SOLVER_MODE_ADMMPD) { - admmpd_copy_from_object(sb->admmpd, ob); + admmpd_copy_from_object(admmpd, ob); } else if (sb->solver_mode == SOLVER_MODE_LEGACY) { softbody_update_positions(ob, sb, vertexCos, numVerts); @@ -3816,10 +3901,10 @@ void sbObjectStep(struct Depsgraph *depsgraph, if (sb->solver_mode == SOLVER_MODE_ADMMPD) { /* We have read the cache into softbody, so we need to pass it to ADMM-PD */ - admmpd_copy_from_object(sb->admmpd, ob); + admmpd_copy_from_object(admmpd, ob); /* Now that we have the updated ADMM-PD vertices, we have * to map them to surface vertices (vertexCos) */ - admmpd_copy_to_object(sb->admmpd, ob, vertexCos); + admmpd_copy_to_object(admmpd, ob, vertexCos); } else if (sb->solver_mode == SOLVER_MODE_LEGACY) { softbody_to_object(ob, vertexCos, numVerts, sb->local); @@ -3858,17 +3943,17 @@ void sbObjectStep(struct Depsgraph *depsgraph, } if (sb->solver_mode == SOLVER_MODE_ADMMPD) { - admmpd_copy_from_object(sb->admmpd, ob); + admmpd_copy_from_object(admmpd, ob); update_collider_admmpd(depsgraph, sb->collision_group, ob); - int solve_retval = admmpd_solve(sb->admmpd, ob, vertexCos); + int solve_retval = admmpd_solve(admmpd, ob, vertexCos); if (solve_retval == 0) { - CLOG_ERROR(&LOG, "%s", sb->admmpd->last_error); - WM_reportf(RPT_ERROR, sb->admmpd->last_error); + CLOG_ERROR(&LOG, "%s", admmpd->last_error); + WM_report(RPT_ERROR, admmpd->last_error); } else if (solve_retval == -1) { - WM_reportf(RPT_WARNING, sb->admmpd->last_error); + WM_report(RPT_WARNING, admmpd->last_error); } - admmpd_copy_to_object(sb->admmpd, ob, vertexCos); + admmpd_copy_to_object(admmpd, ob, vertexCos); } else if (sb->solver_mode == SOLVER_MODE_LEGACY) { softbody_update_positions(ob, sb, vertexCos, numVerts); diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index b72c5e99b43..8ee6a3627dc 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -143,6 +143,11 @@ IDTypeInfo IDType_ID_SO = { .make_local = NULL, .foreach_id = NULL, .foreach_cache = sound_foreach_cache, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; #ifdef WITH_AUDASPACE diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index 1f778f5fcd6..8f403792c46 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -65,6 +65,12 @@ IDTypeInfo IDType_ID_SPK = { .free_data = NULL, .make_local = NULL, .foreach_id = speaker_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; void *BKE_speaker_add(Main *bmain, const char *name) diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 8a055423d6f..bde9b9ab9b8 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -182,6 +182,12 @@ IDTypeInfo IDType_ID_TXT = { .free_data = text_free_data, .make_local = NULL, .foreach_id = NULL, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \} */ diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 8d5a0497e28..9e176f355d3 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -149,6 +149,12 @@ IDTypeInfo IDType_ID_TE = { .free_data = texture_free_data, .make_local = NULL, .foreach_id = texture_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /* Utils for all IDs using those texture slots. */ diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 54fb0f612d1..9e873692486 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -525,6 +525,11 @@ IDTypeInfo IDType_ID_VO = { /* make_local */ nullptr, /* foreach_id */ volume_foreach_id, /* foreach_cache */ volume_foreach_cache, + + /* blend_write */ NULL, + /* blend_read_data */ NULL, + /* blend_read_lib */ NULL, + /* blend_read_expand */ NULL, }; void BKE_volume_init_grids(Volume *volume) diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index f653a190704..e5e6a5d67c8 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -90,6 +90,12 @@ IDTypeInfo IDType_ID_WS = { .free_data = workspace_free_data, .make_local = NULL, .foreach_id = workspace_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; /** \name Internal Utils diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index e3b58e03d93..25c62f139ed 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -137,6 +137,12 @@ IDTypeInfo IDType_ID_WO = { .free_data = world_free_data, .make_local = NULL, .foreach_id = world_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; World *BKE_world_add(Main *bmain, const char *name) diff --git a/source/blender/blenlib/BLI_array.h b/source/blender/blenlib/BLI_array.h index 6ea01d45f79..4e37314ed3e 100644 --- a/source/blender/blenlib/BLI_array.h +++ b/source/blender/blenlib/BLI_array.h @@ -117,7 +117,7 @@ void _bli_array_grow_func(void **arr_p, { \ if (arr && (char *)arr != _##arr##_static) { \ BLI_array_fake_user(arr); \ - MEM_freeN(arr); \ + MEM_freeN((void *)arr); \ } \ } \ ((void)0) diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index 9d09bb3559e..dddf4f64ff5 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -77,32 +77,39 @@ class Array { /** * By default an empty array is created. */ - Array() + Array(Allocator allocator = {}) noexcept : allocator_(allocator) { data_ = inline_buffer_; size_ = 0; } + Array(NoExceptConstructor, Allocator allocator = {}) noexcept : Array(allocator) + { + } + /** * Create a new array that contains copies of all values. */ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> - Array(Span<U> values, Allocator allocator = {}) : allocator_(allocator) + Array(Span<U> values, Allocator allocator = {}) : Array(NoExceptConstructor(), allocator) { - size_ = values.size(); - data_ = this->get_buffer_for_size(values.size()); - uninitialized_convert_n<U, T>(values.data(), size_, data_); + const int64_t size = values.size(); + data_ = this->get_buffer_for_size(size); + uninitialized_convert_n<U, T>(values.data(), size, data_); + size_ = size; } /** * Create a new array that contains copies of all values. */ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> - Array(const std::initializer_list<U> &values) : Array(Span<U>(values)) + Array(const std::initializer_list<U> &values, Allocator allocator = {}) + : Array(Span<U>(values), allocator) { } - Array(const std::initializer_list<T> &values) : Array(Span<T>(values)) + Array(const std::initializer_list<T> &values, Allocator allocator = {}) + : Array(Span<T>(values), allocator) { } @@ -114,23 +121,24 @@ class Array { * even for non-trivial types. This should not be the default though, because one can easily mess * up when dealing with uninitialized memory. */ - explicit Array(int64_t size) + explicit Array(int64_t size, Allocator allocator = {}) : Array(NoExceptConstructor(), allocator) { - size_ = size; data_ = this->get_buffer_for_size(size); default_construct_n(data_, size); + size_ = size; } /** * Create a new array with the given size. All values will be initialized by copying the given * default. */ - Array(int64_t size, const T &value) + Array(int64_t size, const T &value, Allocator allocator = {}) + : Array(NoExceptConstructor(), allocator) { BLI_assert(size >= 0); - size_ = size; data_ = this->get_buffer_for_size(size); - uninitialized_fill_n(data_, size_, value); + uninitialized_fill_n(data_, size, value); + size_ = size; } /** @@ -145,28 +153,28 @@ class Array { * Usage: * Array<std::string> my_strings(10, NoInitialization()); */ - Array(int64_t size, NoInitialization) + Array(int64_t size, NoInitialization, Allocator allocator = {}) + : Array(NoExceptConstructor(), allocator) { BLI_assert(size >= 0); - size_ = size; data_ = this->get_buffer_for_size(size); + size_ = size; } Array(const Array &other) : Array(other.as_span(), other.allocator_) { } - Array(Array &&other) noexcept : allocator_(other.allocator_) + Array(Array &&other) noexcept(std::is_nothrow_move_constructible_v<T>) + : Array(NoExceptConstructor(), other.allocator_) { - size_ = other.size_; - - if (!other.uses_inline_buffer()) { - data_ = other.data_; + if (other.data_ == other.inline_buffer_) { + uninitialized_relocate_n(other.data_, other.size_, data_); } else { - data_ = this->get_buffer_for_size(size_); - uninitialized_relocate_n(other.data_, size_, data_); + data_ = other.data_; } + size_ = other.size_; other.data_ = other.inline_buffer_; other.size_ = 0; @@ -175,31 +183,17 @@ class Array { ~Array() { destruct_n(data_, size_); - if (!this->uses_inline_buffer()) { - allocator_.deallocate(static_cast<void *>(data_)); - } + this->deallocate_if_not_inline(data_); } Array &operator=(const Array &other) { - if (this == &other) { - return *this; - } - - this->~Array(); - new (this) Array(other); - return *this; + return copy_assign_container(*this, other); } - Array &operator=(Array &&other) + Array &operator=(Array &&other) noexcept(std::is_nothrow_move_constructible_v<T>) { - if (this == &other) { - return *this; - } - - this->~Array(); - new (this) Array(std::move(other)); - return *this; + return move_assign_container(*this, std::move(other)); } T &operator[](int64_t index) @@ -273,6 +267,21 @@ class Array { } /** + * Return a reference to the last element in the array. + * This invokes undefined behavior when the array is empty. + */ + const T &last() const + { + BLI_assert(size_ > 0); + return *(data_ + size_ - 1); + } + T &last() + { + BLI_assert(size_ > 0); + return *(data_ + size_ - 1); + } + + /** * Get a pointer to the beginning of the array. */ const T *data() const @@ -354,6 +363,37 @@ class Array { return InlineBufferCapacity; } + /** + * Destruct values and create a new array of the given size. The values in the new array are + * default constructed. + */ + void reinitialize(const int64_t new_size) + { + BLI_assert(new_size >= 0); + int64_t old_size = size_; + + destruct_n(data_, size_); + size_ = 0; + + if (new_size <= old_size) { + default_construct_n(data_, new_size); + } + else { + T *new_data = this->get_buffer_for_size(new_size); + try { + default_construct_n(new_data, new_size); + } + catch (...) { + this->deallocate_if_not_inline(new_data); + throw; + } + this->deallocate_if_not_inline(data_); + data_ = new_data; + } + + size_ = new_size; + } + private: T *get_buffer_for_size(int64_t size) { @@ -371,9 +411,11 @@ class Array { allocator_.allocate(static_cast<size_t>(size) * sizeof(T), alignof(T), AT)); } - bool uses_inline_buffer() const + void deallocate_if_not_inline(T *ptr) { - return data_ == inline_buffer_; + if (ptr != inline_buffer_) { + allocator_.deallocate(ptr); + } } }; diff --git a/source/blender/blenlib/BLI_delaunay_2d.h b/source/blender/blenlib/BLI_delaunay_2d.h index a826a6b2677..7027477ac7f 100644 --- a/source/blender/blenlib/BLI_delaunay_2d.h +++ b/source/blender/blenlib/BLI_delaunay_2d.h @@ -18,6 +18,9 @@ /** \file * \ingroup bli + * + * This header file contains both a C interface and a C++ interface + * to the 2D Constrained Delaunay Triangulation library routine. */ #ifdef __cplusplus @@ -107,11 +110,6 @@ extern "C" { * If zero is supplied for epsilon, an internal value of 1e-8 used * instead, since this code will not work correctly if it is not allowed * to merge "too near" vertices. - * - * Normally, if epsilon is non-zero, there is an "input modify" pass which - * checks to see if some vertices are within epsilon of other edges, and - * snapping them to those edges if so. You can skip this pass by setting - * skip_input_modify to true. (This is also useful in some unit tests.) */ typedef struct CDT_input { int verts_len; @@ -123,7 +121,6 @@ typedef struct CDT_input { int *faces_start_table; int *faces_len_table; float epsilon; - bool skip_input_modify; } CDT_input; /** @@ -150,12 +147,9 @@ typedef struct CDT_input { * - faces_orig, faces_orig_start_table, faces_orig_len_table * * For edges, the edges_orig triple can also say which original face - * edge is part of a given output edge. If an index in edges_orig - * is greater than the input's edges_len, then subtract input's edges_len - * from it to some number i: then the face edge that starts from the - * input vertex at input's faces[i] is the corresponding face edge. - * for convenience, face_edge_offset in the result will be the input's - * edges_len, so that this conversion can be easily done by the caller. + * edge is part of a given output edge. See the comment below + * on the C++ interface for how to decode the entries in the edges_orig + * table. */ typedef struct CDT_result { int verts_len; @@ -207,4 +201,69 @@ void BLI_delaunay_2d_cdt_free(CDT_result *result); #ifdef __cplusplus } -#endif + +/* C++ Interface. */ + +# include "BLI_array.hh" +# include "BLI_double2.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mpq2.hh" +# include "BLI_vector.hh" + +namespace blender::meshintersect { + +/* vec2<Arith_t> is a 2d vector with Arith_t as the type for coordinates. */ +template<typename Arith_t> struct vec2_impl; +template<> struct vec2_impl<double> { + typedef double2 type; +}; + +# ifdef WITH_GMP +template<> struct vec2_impl<mpq_class> { + typedef mpq2 type; +}; +# endif + +template<typename Arith_t> using vec2 = typename vec2_impl<Arith_t>::type; + +template<typename Arith_t> class CDT_input { + public: + Array<vec2<Arith_t>> vert; + Array<std::pair<int, int>> edge; + Array<Vector<int>> face; + Arith_t epsilon{0}; +}; + +template<typename Arith_t> class CDT_result { + public: + Array<vec2<Arith_t>> vert; + Array<std::pair<int, int>> edge; + Array<Vector<int>> face; + /** For each output vert, which input verts correspond to it? */ + Array<Vector<int>> vert_orig; + /** + * For each output edge, which input edges does it overlap? + * The input edge ids are encoded as follows: + * if the value is less than face_edge_offset, then it is + * an index into the input edge[] array. + * else let (a, b) = the quotient and remainder of dividing + * the edge index by face_edge_offset; "a" will be the input face + 1, + * and "b" will be a position within that face. + */ + Array<Vector<int>> edge_orig; + /** For each output face, which original faces does it overlap? */ + Array<Vector<int>> face_orig; + /** Used to encode edge_orig (see above). */ + int face_edge_offset; +}; + +CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input, CDT_output_type output_type); + +# ifdef WITH_GMP +CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input, + CDT_output_type output_type); +# endif + +} /* namespace blender::meshintersect */ + +#endif /* __cplusplus */ diff --git a/source/blender/blenlib/BLI_double2.hh b/source/blender/blenlib/BLI_double2.hh new file mode 100644 index 00000000000..3466b946e73 --- /dev/null +++ b/source/blender/blenlib/BLI_double2.hh @@ -0,0 +1,143 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include "BLI_double3.hh" + +namespace blender { + +struct double2 { + double x, y; + + double2() = default; + + double2(const double *ptr) : x{ptr[0]}, y{ptr[1]} + { + } + + double2(double x, double y) : x(x), y(y) + { + } + + double2(const double3 &other) : x(other.x), y(other.y) + { + } + + operator double *() + { + return &x; + } + + operator const double *() const + { + return &x; + } + + float length() const + { + return len_v2_db(*this); + } + + friend double2 operator+(const double2 &a, const double2 &b) + { + return {a.x + b.x, a.y + b.y}; + } + + friend double2 operator-(const double2 &a, const double2 &b) + { + return {a.x - b.x, a.y - b.y}; + } + + friend double2 operator*(const double2 &a, double b) + { + return {a.x * b, a.y * b}; + } + + friend double2 operator/(const double2 &a, double b) + { + BLI_assert(b != 0.0); + return {a.x / b, a.y / b}; + } + + friend double2 operator*(double a, const double2 &b) + { + return b * a; + } + + friend bool operator==(const double2 &a, const double2 &b) + { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(const double2 &a, const double2 &b) + { + return a.x != b.x || a.y != b.y; + } + + friend std::ostream &operator<<(std::ostream &stream, const double2 &v) + { + stream << "(" << v.x << ", " << v.y << ")"; + return stream; + } + + static double dot(const double2 &a, const double2 &b) + { + return a.x * b.x + a.y * b.y; + } + + static double2 interpolate(const double2 &a, const double2 &b, double t) + { + return a * (1 - t) + b * t; + } + + static double2 abs(const double2 &a) + { + return double2(fabs(a.x), fabs(a.y)); + } + + static double distance(const double2 &a, const double2 &b) + { + return (a - b).length(); + } + + static double distance_squared(const double2 &a, const double2 &b) + { + return double2::dot(a, b); + } + + struct isect_result { + enum { + LINE_LINE_COLINEAR = -1, + LINE_LINE_NONE = 0, + LINE_LINE_EXACT = 1, + LINE_LINE_CROSS = 2, + } kind; + double lambda; + double mu; + }; + + static isect_result isect_seg_seg(const double2 &v1, + const double2 &v2, + const double2 &v3, + const double2 &v4); +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_double3.hh b/source/blender/blenlib/BLI_double3.hh new file mode 100644 index 00000000000..5b6204935d7 --- /dev/null +++ b/source/blender/blenlib/BLI_double3.hh @@ -0,0 +1,245 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#include <iostream> + +#include "BLI_math_vector.h" +#include "BLI_span.hh" + +namespace blender { + +struct double3 { + double x, y, z; + + double3() = default; + + double3(const double *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]} + { + } + + double3(const double (*ptr)[3]) : double3((const double *)ptr) + { + } + + explicit double3(double value) : x(value), y(value), z(value) + { + } + + explicit double3(int value) : x(value), y(value), z(value) + { + } + + double3(double x, double y, double z) : x{x}, y{y}, z{z} + { + } + + operator const double *() const + { + return &x; + } + + operator double *() + { + return &x; + } + + double normalize_and_get_length() + { + return normalize_v3_db(*this); + } + + double3 normalized() const + { + double3 result; + normalize_v3_v3_db(result, *this); + return result; + } + + double length() const + { + return len_v3_db(*this); + } + + double length_squared() const + { + return len_squared_v3_db(*this); + } + + void reflect(const double3 &normal) + { + *this = this->reflected(normal); + } + + double3 reflected(const double3 &normal) const + { + double3 result; + reflect_v3_v3v3_db(result, *this, normal); + return result; + } + + static double3 safe_divide(const double3 &a, const double3 &b) + { + double3 result; + result.x = (b.x == 0.0) ? 0.0 : a.x / b.x; + result.y = (b.y == 0.0) ? 0.0 : a.y / b.y; + result.z = (b.z == 0.0) ? 0.0 : a.z / b.z; + return result; + } + + void invert() + { + x = -x; + y = -y; + z = -z; + } + + friend double3 operator+(const double3 &a, const double3 &b) + { + return {a.x + b.x, a.y + b.y, a.z + b.z}; + } + + void operator+=(const double3 &b) + { + this->x += b.x; + this->y += b.y; + this->z += b.z; + } + + friend double3 operator-(const double3 &a, const double3 &b) + { + return {a.x - b.x, a.y - b.y, a.z - b.z}; + } + + friend double3 operator-(const double3 &a) + { + return {-a.x, -a.y, -a.z}; + } + + void operator-=(const double3 &b) + { + this->x -= b.x; + this->y -= b.y; + this->z -= b.z; + } + + void operator*=(const double &scalar) + { + this->x *= scalar; + this->y *= scalar; + this->z *= scalar; + } + + void operator*=(const double3 &other) + { + this->x *= other.x; + this->y *= other.y; + this->z *= other.z; + } + + friend double3 operator*(const double3 &a, const double3 &b) + { + return {a.x * b.x, a.y * b.y, a.z * b.z}; + } + + friend double3 operator*(const double3 &a, const double &b) + { + return {a.x * b, a.y * b, a.z * b}; + } + + friend double3 operator*(const double &a, const double3 &b) + { + return b * a; + } + + friend double3 operator/(const double3 &a, const double &b) + { + BLI_assert(b != 0.0); + return {a.x / b, a.y / b, a.z / b}; + } + + friend bool operator==(const double3 &a, const double3 &b) + { + return a.x == b.x && a.y == b.y && a.z == b.z; + } + + friend bool operator!=(const double3 &a, const double3 &b) + { + return a.x != b.x || a.y != b.y || a.z != b.z; + } + + friend std::ostream &operator<<(std::ostream &stream, const double3 &v) + { + stream << "(" << v.x << ", " << v.y << ", " << v.z << ")"; + return stream; + } + + static double dot(const double3 &a, const double3 &b) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + static double3 cross_high_precision(const double3 &a, const double3 &b) + { + double3 result; + cross_v3_v3v3_db(result, a, b); + return result; + } + + static double3 project(const double3 &a, const double3 &b) + { + double3 result; + project_v3_v3v3_db(result, a, b); + return result; + } + + static double distance(const double3 &a, const double3 &b) + { + return (a - b).length(); + } + + static double distance_squared(const double3 &a, const double3 &b) + { + return double3::dot(a, b); + } + + static double3 interpolate(const double3 &a, const double3 &b, double t) + { + return a * (1 - t) + b * t; + } + + static double3 abs(const double3 &a) + { + return double3(fabs(a.x), fabs(a.y), fabs(a.z)); + } + + static int dominant_axis(const double3 &a) + { + double x = (a.x >= 0) ? a.x : -a.x; + double y = (a.y >= 0) ? a.y : -a.y; + double z = (a.z >= 0) ? a.z : -a.z; + return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2)); + } + + static double3 cross_poly(Span<double3> poly); +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh index e55a8de4633..1290eb6c65c 100644 --- a/source/blender/blenlib/BLI_float2.hh +++ b/source/blender/blenlib/BLI_float2.hh @@ -47,6 +47,11 @@ struct float2 { return &x; } + float length() const + { + return len_v2(*this); + } + float2 &operator+=(const float2 &other) { x += other.x; @@ -107,6 +112,47 @@ struct float2 { return stream; } + static float dot(const float2 &a, const float2 &b) + { + return a.x * b.x + a.y * b.y; + } + + static float2 interpolate(const float2 &a, const float2 &b, float t) + { + return a * (1 - t) + b * t; + } + + static float2 abs(const float2 &a) + { + return float2(fabsf(a.x), fabsf(a.y)); + } + + static float distance(const float2 &a, const float2 &b) + { + return (a - b).length(); + } + + static float distance_squared(const float2 &a, const float2 &b) + { + return float2::dot(a, b); + } + + struct isect_result { + enum { + LINE_LINE_COLINEAR = -1, + LINE_LINE_NONE = 0, + LINE_LINE_EXACT = 1, + LINE_LINE_CROSS = 2, + } kind; + float lambda; + float mu; + }; + + static isect_result isect_seg_seg(const float2 &v1, + const float2 &v2, + const float2 &v3, + const float2 &v4); + friend bool operator==(const float2 &a, const float2 &b) { return a.x == b.x && a.y == b.y; diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 2d90498fee8..17b3f56453c 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -243,6 +243,11 @@ struct float3 { { return a * (1 - t) + b * t; } + + static float3 abs(const float3 &a) + { + return float3(fabsf(a.x), fabsf(a.y), fabsf(a.z)); + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_map.hh b/source/blender/blenlib/BLI_map.hh index 229bbfad0e4..08fe1a3cdbc 100644 --- a/source/blender/blenlib/BLI_map.hh +++ b/source/blender/blenlib/BLI_map.hh @@ -171,14 +171,18 @@ class Map { * This is necessary to avoid a high cost when no elements are added at all. An optimized grow * operation is performed on the first insertion. */ - Map() + Map(Allocator allocator = {}) noexcept : removed_slots_(0), occupied_and_removed_slots_(0), usable_slots_(0), slot_mask_(0), hash_(), is_equal_(), - slots_(1) + slots_(1, allocator) + { + } + + Map(NoExceptConstructor, Allocator allocator = {}) noexcept : Map(allocator) { } @@ -186,41 +190,38 @@ class Map { Map(const Map &other) = default; - Map(Map &&other) noexcept - : removed_slots_(other.removed_slots_), - occupied_and_removed_slots_(other.occupied_and_removed_slots_), - usable_slots_(other.usable_slots_), - slot_mask_(other.slot_mask_), - hash_(std::move(other.hash_)), - is_equal_(std::move(other.is_equal_)), - slots_(std::move(other.slots_)) + Map(Map &&other) noexcept(std::is_nothrow_move_constructible_v<SlotArray>) + : Map(NoExceptConstructor(), other.slots_.allocator()) { - other.~Map(); - new (&other) Map(); + if constexpr (std::is_nothrow_move_constructible_v<SlotArray>) { + slots_ = std::move(other.slots_); + } + else { + try { + slots_ = std::move(other.slots_); + } + catch (...) { + other.noexcept_reset(); + throw; + } + } + removed_slots_ = other.removed_slots_; + occupied_and_removed_slots_ = other.occupied_and_removed_slots_; + usable_slots_ = other.usable_slots_; + slot_mask_ = other.slot_mask_; + hash_ = std::move(other.hash_); + is_equal_ = std::move(other.is_equal_); + other.noexcept_reset(); } Map &operator=(const Map &other) { - if (this == &other) { - return *this; - } - - this->~Map(); - new (this) Map(other); - - return *this; + return copy_assign_container(*this, other); } Map &operator=(Map &&other) { - if (this == &other) { - return *this; - } - - this->~Map(); - new (this) Map(std::move(other)); - - return *this; + return move_assign_container(*this, std::move(other)); } /** @@ -315,7 +316,7 @@ class Map { } template<typename ForwardKey> bool contains_as(const ForwardKey &key) const { - return this->contains__impl(key, hash_(key)); + return this->lookup_slot_ptr(key, hash_(key)) != nullptr; } /** @@ -330,7 +331,13 @@ class Map { } template<typename ForwardKey> bool remove_as(const ForwardKey &key) { - return this->remove__impl(key, hash_(key)); + Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + if (slot == nullptr) { + return false; + } + slot->remove(); + removed_slots_++; + return true; } /** @@ -343,7 +350,9 @@ class Map { } template<typename ForwardKey> void remove_contained_as(const ForwardKey &key) { - this->remove_contained__impl(key, hash_(key)); + Slot &slot = this->lookup_slot(key, hash_(key)); + slot.remove(); + removed_slots_++; } /** @@ -356,7 +365,11 @@ class Map { } template<typename ForwardKey> Value pop_as(const ForwardKey &key) { - return this->pop__impl(key, hash_(key)); + Slot &slot = this->lookup_slot(key, hash_(key)); + Value value = std::move(*slot.value()); + slot.remove(); + removed_slots_++; + return value; } /** @@ -369,7 +382,14 @@ class Map { } template<typename ForwardKey> std::optional<Value> pop_try_as(const ForwardKey &key) { - return this->pop_try__impl(key, hash_(key)); + Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + if (slot == nullptr) { + return {}; + } + std::optional<Value> value = std::move(*slot->value()); + slot->remove(); + removed_slots_++; + return value; } /** @@ -387,7 +407,14 @@ class Map { template<typename ForwardKey, typename ForwardValue> Value pop_default_as(const ForwardKey &key, ForwardValue &&default_value) { - return this->pop_default__impl(key, std::forward<ForwardValue>(default_value), hash_(key)); + Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + if (slot == nullptr) { + return std::forward<ForwardValue>(default_value); + } + Value value = std::move(*slot->value()); + slot->remove(); + removed_slots_++; + return value; } /** @@ -448,11 +475,12 @@ class Map { } template<typename ForwardKey> const Value *lookup_ptr_as(const ForwardKey &key) const { - return this->lookup_ptr__impl(key, hash_(key)); + const Slot *slot = this->lookup_slot_ptr(key, hash_(key)); + return (slot != nullptr) ? slot->value() : nullptr; } template<typename ForwardKey> Value *lookup_ptr_as(const ForwardKey &key) { - return const_cast<Value *>(this->lookup_ptr__impl(key, hash_(key))); + return const_cast<Value *>(const_cast<const Map *>(this)->lookup_ptr_as(key)); } /** @@ -843,8 +871,7 @@ class Map { */ void clear() { - this->~Map(); - new (this) Map(); + this->noexcept_reset(); } /** @@ -869,8 +896,13 @@ class Map { * Optimize the case when the map was empty beforehand. We can avoid some copies here. */ if (this->size() == 0) { - slots_.~Array(); - new (&slots_) SlotArray(total_slots); + try { + slots_.reinitialize(total_slots); + } + catch (...) { + this->noexcept_reset(); + throw; + } removed_slots_ = 0; occupied_and_removed_slots_ = 0; usable_slots_ = usable_slots; @@ -880,48 +912,44 @@ class Map { SlotArray new_slots(total_slots); - for (Slot &slot : slots_) { - if (slot.is_occupied()) { - this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask); + try { + for (Slot &slot : slots_) { + if (slot.is_occupied()) { + this->add_after_grow(slot, new_slots, new_slot_mask); + slot.remove(); + } } + slots_ = std::move(new_slots); + } + catch (...) { + this->noexcept_reset(); + throw; } - /* All occupied slots have been destructed already and empty/removed slots are assumed to be - * trivially destructible. */ - slots_.clear_without_destruct(); - slots_ = std::move(new_slots); occupied_and_removed_slots_ -= removed_slots_; usable_slots_ = usable_slots; removed_slots_ = 0; slot_mask_ = new_slot_mask; } - void add_after_grow_and_destruct_old(Slot &old_slot, - SlotArray &new_slots, - uint64_t new_slot_mask) + void add_after_grow(Slot &old_slot, SlotArray &new_slots, uint64_t new_slot_mask) { uint64_t hash = old_slot.get_hash(Hash()); SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) { Slot &slot = new_slots[slot_index]; if (slot.is_empty()) { - slot.relocate_occupied_here(old_slot, hash); + slot.occupy(std::move(*old_slot.key()), std::move(*old_slot.value()), hash); return; } } SLOT_PROBING_END(); } - template<typename ForwardKey> bool contains__impl(const ForwardKey &key, uint64_t hash) const + void noexcept_reset() noexcept { - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.is_empty()) { - return false; - } - if (slot.contains(key, is_equal_, hash)) { - return true; - } - } - MAP_SLOT_PROBING_END(); + Allocator allocator = slots_.allocator(); + this->~Map(); + new (this) Map(NoExceptConstructor(), allocator); } template<typename ForwardKey, typename ForwardValue> @@ -930,11 +958,11 @@ class Map { BLI_assert(!this->contains_as(key)); this->ensure_can_add(); - occupied_and_removed_slots_++; MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { slot.occupy(std::forward<ForwardKey>(key), std::forward<ForwardValue>(value), hash); + occupied_and_removed_slots_++; return; } } @@ -959,86 +987,6 @@ class Map { MAP_SLOT_PROBING_END(); } - template<typename ForwardKey> bool remove__impl(const ForwardKey &key, uint64_t hash) - { - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - slot.remove(); - removed_slots_++; - return true; - } - if (slot.is_empty()) { - return false; - } - } - MAP_SLOT_PROBING_END(); - } - - template<typename ForwardKey> void remove_contained__impl(const ForwardKey &key, uint64_t hash) - { - BLI_assert(this->contains_as(key)); - - removed_slots_++; - - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - slot.remove(); - return; - } - } - MAP_SLOT_PROBING_END(); - } - - template<typename ForwardKey> Value pop__impl(const ForwardKey &key, uint64_t hash) - { - BLI_assert(this->contains_as(key)); - - removed_slots_++; - - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - Value value = std::move(*slot.value()); - slot.remove(); - return value; - } - } - MAP_SLOT_PROBING_END(); - } - - template<typename ForwardKey> - std::optional<Value> pop_try__impl(const ForwardKey &key, uint64_t hash) - { - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - std::optional<Value> value = std::move(*slot.value()); - slot.remove(); - removed_slots_++; - return value; - } - if (slot.is_empty()) { - return {}; - } - } - MAP_SLOT_PROBING_END(); - } - - template<typename ForwardKey, typename ForwardValue> - Value pop_default__impl(const ForwardKey &key, ForwardValue &&default_value, uint64_t hash) - { - MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.contains(key, is_equal_, hash)) { - Value value = std::move(*slot.value()); - slot.remove(); - removed_slots_++; - return value; - } - if (slot.is_empty()) { - return std::forward<ForwardValue>(default_value); - } - } - MAP_SLOT_PROBING_END(); - } - template<typename ForwardKey, typename CreateValueF, typename ModifyValueF> auto add_or_modify__impl(ForwardKey &&key, const CreateValueF &create_value, @@ -1054,10 +1002,19 @@ class Map { MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.is_empty()) { - occupied_and_removed_slots_++; - slot.occupy_without_value(std::forward<ForwardKey>(key), hash); Value *value_ptr = slot.value(); - return create_value(value_ptr); + if constexpr (std::is_void_v<CreateReturnT>) { + create_value(value_ptr); + slot.occupy_no_value(std::forward<ForwardKey>(key), hash); + occupied_and_removed_slots_++; + return; + } + else { + auto return_value = create_value(value_ptr); + slot.occupy_no_value(std::forward<ForwardKey>(key), hash); + occupied_and_removed_slots_++; + return return_value; + } } if (slot.contains(key, is_equal_, hash)) { Value *value_ptr = slot.value(); @@ -1119,19 +1076,41 @@ class Map { } template<typename ForwardKey> - const Value *lookup_ptr__impl(const ForwardKey &key, uint64_t hash) const + const Slot &lookup_slot(const ForwardKey &key, const uint64_t hash) const { + BLI_assert(this->contains_as(key)); MAP_SLOT_PROBING_BEGIN (hash, slot) { - if (slot.is_empty()) { - return nullptr; + if (slot.contains(key, is_equal_, hash)) { + return slot; } + } + MAP_SLOT_PROBING_END(); + } + + template<typename ForwardKey> Slot &lookup_slot(const ForwardKey &key, const uint64_t hash) + { + return const_cast<Slot &>(const_cast<const Map *>(this)->lookup_slot(key, hash)); + } + + template<typename ForwardKey> + const Slot *lookup_slot_ptr(const ForwardKey &key, const uint64_t hash) const + { + MAP_SLOT_PROBING_BEGIN (hash, slot) { if (slot.contains(key, is_equal_, hash)) { - return slot.value(); + return &slot; + } + if (slot.is_empty()) { + return nullptr; } } MAP_SLOT_PROBING_END(); } + template<typename ForwardKey> Slot *lookup_slot_ptr(const ForwardKey &key, const uint64_t hash) + { + return const_cast<Slot *>(const_cast<const Map *>(this)->lookup_slot_ptr(key, hash)); + } + template<typename ForwardKey> int64_t count_collisions__impl(const ForwardKey &key, uint64_t hash) const { diff --git a/source/blender/blenlib/BLI_map_slots.hh b/source/blender/blenlib/BLI_map_slots.hh index 25fb92d61a3..c0cb3091a8e 100644 --- a/source/blender/blenlib/BLI_map_slots.hh +++ b/source/blender/blenlib/BLI_map_slots.hh @@ -38,6 +38,19 @@ namespace blender { +template<typename Src1, typename Src2, typename Dst1, typename Dst2> +void initialize_pointer_pair(Src1 &&src1, Src2 &&src2, Dst1 *dst1, Dst2 *dst2) +{ + new ((void *)dst1) Dst1(std::forward<Src1>(src1)); + try { + new ((void *)dst2) Dst2(std::forward<Src2>(src2)); + } + catch (...) { + dst1->~Dst1(); + throw; + } +} + /** * The simplest possible map slot. It stores the slot state and the optional key and value * instances in separate variables. Depending on the alignment requirement of the key and value, @@ -83,8 +96,10 @@ template<typename Key, typename Value> class SimpleMapSlot { { state_ = other.state_; if (other.state_ == Occupied) { - new (&key_buffer_) Key(*other.key_buffer_); - new (&value_buffer_) Value(*other.value_buffer_); + initialize_pointer_pair(other.key_buffer_.ref(), + other.value_buffer_.ref(), + key_buffer_.ptr(), + value_buffer_.ptr()); } } @@ -93,12 +108,15 @@ template<typename Key, typename Value> class SimpleMapSlot { * from the other have to moved as well. The other slot stays in the state it was in before. Its * optionally stored key and value remain in a moved-from state. */ - SimpleMapSlot(SimpleMapSlot &&other) noexcept + SimpleMapSlot(SimpleMapSlot &&other) noexcept( + std::is_nothrow_move_constructible_v<Key> &&std::is_nothrow_move_constructible_v<Value>) { state_ = other.state_; if (other.state_ == Occupied) { - new (&key_buffer_) Key(std::move(*other.key_buffer_)); - new (&value_buffer_) Value(std::move(*other.value_buffer_)); + initialize_pointer_pair(std::move(other.key_buffer_.ref()), + std::move(other.value_buffer_.ref()), + key_buffer_.ptr(), + value_buffer_.ptr()); } } @@ -161,21 +179,6 @@ template<typename Key, typename Value> class SimpleMapSlot { } /** - * Move the other slot into this slot and destruct it. We do destruction here, because this way - * we can avoid a comparison with the state, since we know the slot is occupied. - */ - void relocate_occupied_here(SimpleMapSlot &other, uint64_t UNUSED(hash)) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - state_ = Occupied; - new (&key_buffer_) Key(std::move(*other.key_buffer_)); - new (&value_buffer_) Value(std::move(*other.value_buffer_)); - other.key_buffer_.ref().~Key(); - other.value_buffer_.ref().~Value(); - } - - /** * Returns true, when this slot is occupied and contains a key that compares equal to the given * key. The hash can be used by other slot implementations to determine inequality faster. */ @@ -196,19 +199,27 @@ template<typename Key, typename Value> class SimpleMapSlot { void occupy(ForwardKey &&key, ForwardValue &&value, uint64_t hash) { BLI_assert(!this->is_occupied()); - this->occupy_without_value(std::forward<ForwardKey>(key), hash); new (&value_buffer_) Value(std::forward<ForwardValue>(value)); + this->occupy_no_value(std::forward<ForwardKey>(key), hash); + state_ = Occupied; } /** - * Change the state of this slot from empty/removed to occupied, but leave the value - * uninitialized. The caller is responsible to construct the value afterwards. + * Change the state of this slot from empty/removed to occupied. The value is assumed to be + * constructed already. */ - template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint64_t UNUSED(hash)) + template<typename ForwardKey> void occupy_no_value(ForwardKey &&key, uint64_t UNUSED(hash)) { BLI_assert(!this->is_occupied()); + try { + new (&key_buffer_) Key(std::forward<ForwardKey>(key)); + } + catch (...) { + /* The value is assumed to be constructed already, so it has to be destructed as well. */ + value_buffer_.ref().~Value(); + throw; + } state_ = Occupied; - new (&key_buffer_) Key(std::forward<ForwardKey>(key)); } /** @@ -218,17 +229,17 @@ template<typename Key, typename Value> class SimpleMapSlot { void remove() { BLI_assert(this->is_occupied()); - state_ = Removed; key_buffer_.ref().~Key(); value_buffer_.ref().~Value(); + state_ = Removed; } }; /** - * An IntrusiveMapSlot uses two special values of the key to indicate whether the slot is empty or - * removed. This saves some memory in all cases and is more efficient in many cases. The KeyInfo - * type indicates which specific values are used. An example for a KeyInfo implementation is - * PointerKeyInfo. + * An IntrusiveMapSlot uses two special values of the key to indicate whether the slot is empty + * or removed. This saves some memory in all cases and is more efficient in many cases. The + * KeyInfo type indicates which specific values are used. An example for a KeyInfo + * implementation is PointerKeyInfo. * * The special key values are expected to be trivially destructible. */ @@ -297,16 +308,6 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot return hash(key_); } - void relocate_occupied_here(IntrusiveMapSlot &other, uint64_t UNUSED(hash)) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - key_ = std::move(other.key_); - new (&value_buffer_) Value(std::move(*other.value_buffer_)); - other.key_.~Key(); - other.value_buffer_.ref().~Value(); - } - template<typename ForwardKey, typename IsEqual> bool contains(const ForwardKey &key, const IsEqual &is_equal, uint64_t UNUSED(hash)) const { @@ -319,22 +320,28 @@ template<typename Key, typename Value, typename KeyInfo> class IntrusiveMapSlot { BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); - this->occupy_without_value(std::forward<ForwardKey>(key), hash); new (&value_buffer_) Value(std::forward<ForwardValue>(value)); + this->occupy_no_value(std::forward<ForwardKey>(key), hash); } - template<typename ForwardKey> void occupy_without_value(ForwardKey &&key, uint64_t UNUSED(hash)) + template<typename ForwardKey> void occupy_no_value(ForwardKey &&key, uint64_t UNUSED(hash)) { BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); - key_ = std::forward<ForwardKey>(key); + try { + key_ = std::forward<ForwardKey>(key); + } + catch (...) { + value_buffer_.ref().~Value(); + throw; + } } void remove() { BLI_assert(this->is_occupied()); - KeyInfo::remove(key_); value_buffer_.ref().~Value(); + KeyInfo::remove(key_); } }; diff --git a/source/blender/blenlib/BLI_math_base.h b/source/blender/blenlib/BLI_math_base.h index f407da3133f..d2bb60717ca 100644 --- a/source/blender/blenlib/BLI_math_base.h +++ b/source/blender/blenlib/BLI_math_base.h @@ -242,6 +242,14 @@ double double_round(double x, int ndigits); } \ (void)0 +# define BLI_ASSERT_UNIT_V3_DB(v) \ + { \ + const double _test_unit = len_squared_v3_db(v); \ + BLI_assert(!(fabs(_test_unit - 1.0) >= BLI_ASSERT_UNIT_EPSILON) || \ + !(fabs(_test_unit) >= BLI_ASSERT_UNIT_EPSILON)); \ + } \ + (void)0 + # define BLI_ASSERT_UNIT_V2(v) \ { \ const float _test_unit = len_squared_v2(v); \ diff --git a/source/blender/blenlib/BLI_math_boolean.hh b/source/blender/blenlib/BLI_math_boolean.hh new file mode 100644 index 00000000000..79b1483bfb8 --- /dev/null +++ b/source/blender/blenlib/BLI_math_boolean.hh @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + * \brief Math vector functions needed specifically for mesh intersect and boolean. + */ + +#include "BLI_double2.hh" +#include "BLI_double3.hh" + +#ifdef WITH_GMP +# include "BLI_math_mpq.hh" +# include "BLI_mpq2.hh" +# include "BLI_mpq3.hh" +#endif + +namespace blender { + +/* #orient2d gives the exact result, using multi-precision arithmetic when result + * is close to zero. orient3d_fast just uses double arithmetic, so may be + * wrong if the answer is very close to zero. + * Similarly, for #incircle and #incircle_fast. */ +int orient2d(const double2 &a, const double2 &b, const double2 &c); +int orient2d_fast(const double2 &a, const double2 &b, const double2 &c); + +int incircle(const double2 &a, const double2 &b, const double2 &c, const double2 &d); +int incircle_fast(const double2 &a, const double2 &b, const double2 &c, const double2 &d); + +/* #orient3d gives the exact result, using multi-precision arithmetic when result + * is close to zero. orient3d_fast just uses double arithmetic, so may be + * wrong if the answer is very close to zero. + * Similarly, for #insphere and #insphere_fast. */ +int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d); +int orient3d_fast(const double3 &a, const double3 &b, const double3 &c, const double3 &d); + +int insphere( + const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e); +int insphere_fast( + const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e); + +#ifdef WITH_GMP +int orient2d(const mpq2 &a, const mpq2 &b, const mpq2 &c); +int incircle(const mpq2 &a, const mpq2 &b, const mpq2 &c, const mpq2 &d); +int orient3d(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &d); +#endif +} // namespace blender diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index a00fdaa0ae9..09f2d5a7cdc 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -299,6 +299,9 @@ bool has_zero_axis_m4(const float matrix[4][4]); void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4]); +void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3]); +void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]); + /****************************** Transformations ******************************/ void scale_m3_fl(float R[3][3], float scale); diff --git a/source/blender/blenlib/BLI_math_mpq.hh b/source/blender/blenlib/BLI_math_mpq.hh new file mode 100644 index 00000000000..3d8c6349187 --- /dev/null +++ b/source/blender/blenlib/BLI_math_mpq.hh @@ -0,0 +1,36 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef WITH_GMP + +/* This file uses an external file header to define the multi-precision + * rational type, mpq_class. + * This class keeps separate multi-precision integer numerator and + * denominator, reduced to lowest terms after each arithmetic operation. + * It can be used where it is important to have exact arithmetic results. + * + * See gmplib.org for full documentation. In particular: + * https://gmplib.org/manual/C_002b_002b-Interface-Rationals + */ +# include "gmpxx.h" + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 1ccfe5d86b1..aa639cd3a5d 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -140,6 +140,7 @@ MINLINE void mul_v2_v2fl(float r[2], const float a[2], float f); MINLINE void mul_v3_fl(float r[3], float f); MINLINE void mul_v3db_db(double r[3], double f); MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f); +MINLINE void mul_v3_v3db_db(double r[3], const double a[3], double f); MINLINE void mul_v2_v2(float r[2], const float a[2]); MINLINE void mul_v2_v2v2(float r[2], const float a[2], const float b[2]); MINLINE void mul_v3_v3(float r[3], const float a[3]); @@ -236,17 +237,21 @@ MINLINE float len_manhattan_v3v3(const float a[3], const float b[3]) ATTR_WARN_U MINLINE float len_v3(const float a[3]) ATTR_WARN_UNUSED_RESULT; MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT; +MINLINE double len_v3_db(const double a[3]) ATTR_WARN_UNUSED_RESULT; +MINLINE double len_squared_v3_db(const double v[3]) ATTR_WARN_UNUSED_RESULT; MINLINE float normalize_v2_length(float r[2], const float unit_scale); MINLINE float normalize_v2_v2_length(float r[2], const float a[2], const float unit_scale); MINLINE float normalize_v3_length(float r[3], const float unit_scale); MINLINE float normalize_v3_v3_length(float r[3], const float a[3], const float unit_scale); -MINLINE double normalize_v3_length_d(double n[3], const double unit_scale); +MINLINE double normalize_v3_length_db(double n[3], const double unit_scale); +MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], const double unit_scale); MINLINE float normalize_v2(float r[2]); MINLINE float normalize_v2_v2(float r[2], const float a[2]); MINLINE float normalize_v3(float r[3]); MINLINE float normalize_v3_v3(float r[3], const float a[3]); -MINLINE double normalize_v3_d(double n[3]); +MINLINE double normalize_v3_v3_db(double r[3], const double a[3]); +MINLINE double normalize_v3_db(double n[3]); /******************************* Interpolation *******************************/ @@ -402,6 +407,7 @@ void angle_poly_v3(float *angles, const float *verts[3], int len); void project_v2_v2v2(float out[2], const float p[2], const float v_proj[2]); void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3]); +void project_v3_v3v3_db(double out[3], const double p[3], const double v_proj[3]); void project_v2_v2v2_normalized(float out[2], const float p[2], const float v_proj[2]); void project_v3_v3v3_normalized(float out[3], const float p[3], const float v_proj[3]); void project_plane_v3_v3v3(float out[3], const float p[3], const float v_plane[3]); @@ -410,6 +416,7 @@ void project_plane_normalized_v3_v3v3(float out[3], const float p[3], const floa void project_plane_normalized_v2_v2v2(float out[2], const float p[2], const float v_plane[2]); void project_v3_plane(float out[3], const float plane_no[3], const float plane_co[3]); void reflect_v3_v3v3(float out[3], const float vec[3], const float normal[3]); +void reflect_v3_v3v3_db(double out[3], const double vec[3], const double normal[3]); void ortho_basis_v3v3_v3(float r_n1[3], float r_n2[3], const float n[3]); void ortho_v3_v3(float out[3], const float v[3]); void ortho_v2_v2(float out[2], const float v[2]); diff --git a/source/blender/blenlib/BLI_memory_utils.hh b/source/blender/blenlib/BLI_memory_utils.hh index 7216536a884..49076bb1aae 100644 --- a/source/blender/blenlib/BLI_memory_utils.hh +++ b/source/blender/blenlib/BLI_memory_utils.hh @@ -162,7 +162,7 @@ void uninitialized_convert_n(const From *src, int64_t n, To *dst) int64_t current = 0; try { for (; current < n; current++) { - new (static_cast<void *>(dst + current)) To((To)src[current]); + new (static_cast<void *>(dst + current)) To(static_cast<To>(src[current])); } } catch (...) { @@ -410,6 +410,15 @@ class NoInitialization { }; /** + * This can be used to mark a constructor of an object that does not throw exceptions. Other + * constructors can delegate to this constructor to make sure that the object lifetime starts. + * With this, the destructor of the object will be called, even when the remaining constructor + * throws. + */ +class NoExceptConstructor { +}; + +/** * Helper variable that checks if a pointer type can be converted into another pointer type without * issues. Possible issues are casting away const and casting a pointer to a child class. * Adding const or casting to a parent class is fine. @@ -427,4 +436,49 @@ inline constexpr int64_t default_inline_buffer_capacity(size_t element_size) return (static_cast<int64_t>(element_size) < 100) ? 4 : 0; } +/** + * This can be used by containers to implement an exception-safe copy-assignment-operator. + * It assumes that the container has an exception safe copy constructor and an exception-safe + * move-assignment-operator. + */ +template<typename Container> Container ©_assign_container(Container &dst, const Container &src) +{ + if (&src == &dst) { + return dst; + } + + Container container_copy{src}; + dst = std::move(container_copy); + return dst; +} + +/** + * This can be used by containers to implement an exception-safe move-assignment-operator. + * It assumes that the container has an exception-safe move-constructor and a noexcept constructor + * tagged with the NoExceptConstructor tag. + */ +template<typename Container> +Container &move_assign_container(Container &dst, Container &&src) noexcept( + std::is_nothrow_move_constructible_v<Container>) +{ + if (&dst == &src) { + return dst; + } + + dst.~Container(); + if constexpr (std::is_nothrow_move_constructible_v<Container>) { + new (&dst) Container(std::move(src)); + } + else { + try { + new (&dst) Container(std::move(src)); + } + catch (...) { + new (&dst) Container(NoExceptConstructor()); + throw; + } + } + return dst; +} + } // namespace blender diff --git a/source/blender/blenlib/BLI_mesh_boolean.hh b/source/blender/blenlib/BLI_mesh_boolean.hh new file mode 100644 index 00000000000..693639f20f2 --- /dev/null +++ b/source/blender/blenlib/BLI_mesh_boolean.hh @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +/* The boolean functions in Blenlib use exact arithmetic, so require GMP. */ +#ifdef WITH_GMP + +# include "BLI_mesh_intersect.hh" +# include <functional> + +namespace blender::meshintersect { + +/** + * Enum values after BOOLEAN_NONE need to match BMESH_ISECT_BOOLEAN_... values in + * editmesh_intersect.c. */ +enum class BoolOpType { + None = -1, + /* Aligned with #BooleanModifierOp. */ + Intersect = 0, + Union = 1, + Difference = 2, +}; + +/** + * Do the boolean operation op on the mesh pm_in. + * The boolean operation has nshapes input shapes. Each is a disjoint subset of the input mesh. + * The shape_fn argument, when applied to an input face argument, says which shape it is in + * (should be a value from -1 to nshapes - 1: if -1, it is not part of any shape). + * The use_self arg says whether or not the function should assume that faces in the + * same shape intersect - if the argument is true, such self-intersections will be found. + * Sometimes the caller has already done a triangulation of the faces, + * and if so, *pm_triangulated contains a triangulation: if non-null, it contains a mesh + * of triangles, each of whose orig_field says which face in pm that triangle belongs to. + * pm arg isn't const because we may populate its verts (for debugging). + * Same goes for the pm_triangulated arg. + * The output IMesh will have faces whose orig fields map back to faces and edges in + * the input mesh. + */ +IMesh boolean_mesh(IMesh &imesh, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMesh *pm_triangulated, + IMeshArena *arena); + +/** + * This is like boolean, but operates on IMesh's whose faces are all triangles. + * It is exposed mainly for unit testing, at the moment: boolean_mesh() uses + * it to do most of its work. + */ +IMesh boolean_trimesh(IMesh &trimesh, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMeshArena *arena); + +} // namespace blender::meshintersect + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_mesh_intersect.hh b/source/blender/blenlib/BLI_mesh_intersect.hh new file mode 100644 index 00000000000..877363b998a --- /dev/null +++ b/source/blender/blenlib/BLI_mesh_intersect.hh @@ -0,0 +1,359 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + * + * Mesh intersection library functions. + * Uses exact arithmetic, so need GMP. + */ + +#ifdef WITH_GMP + +# include <iostream> + +# include "BLI_array.hh" +# include "BLI_double3.hh" +# include "BLI_index_range.hh" +# include "BLI_map.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mpq3.hh" +# include "BLI_span.hh" +# include "BLI_utility_mixins.hh" +# include "BLI_vector.hh" + +namespace blender::meshintersect { + +constexpr int NO_INDEX = -1; + +/** + * Vertex coordinates are stored both as #double3 and #mpq3, which should agree. + * Most calculations are done in exact arithmetic, using the mpq3 version, + * but some predicates can be sped up by operating on doubles and using error analysis + * to find the cases where that is good enough. + * Vertices also carry along an id, created on allocation. The id + * is useful for making algorithms that don't depend on pointers. + * Also, they are easier to read while debugging. + * They also carry an orig index, which can be used to tie them back to + * vertices that the caller may have in a different way (e.g., #BMVert). + * An orig index can be #NO_INDEX, indicating the Vert was created by + * the algorithm and doesn't match an original Vert. + * Vertices can be reliably compared for equality, + * and hashed (on their co_exact field). + */ +struct Vert { + mpq3 co_exact; + double3 co; + int id = NO_INDEX; + int orig = NO_INDEX; + + Vert() = default; + Vert(const mpq3 &mco, const double3 &dco, int id, int orig); + ~Vert() = default; + + /** Test equality on the co_exact field. */ + bool operator==(const Vert &other) const; + + /** Hash on the co_exact field. */ + uint64_t hash() const; +}; + +std::ostream &operator<<(std::ostream &os, const Vert *v); + +/** + * A Plane whose equation is `dot(norm, p) + d = 0`. + * The norm and d fields are always present, but the norm_exact + * and d_exact fields may be lazily populated. Since we don't + * store degenerate planes, we can tell if a the exact versions + * are not populated yet by having `norm_exact == 0`. + */ +struct Plane { + mpq3 norm_exact; + mpq_class d_exact; + double3 norm; + double d; + + Plane() = default; + Plane(const mpq3 &norm_exact, const mpq_class &d_exact); + Plane(const double3 &norm, const double d); + + /* Test equality on the exact fields. */ + bool operator==(const Plane &other) const; + + /* Hash onthe exact fields. */ + uint64_t hash() const; + + void make_canonical(); + bool exact_populated() const; + void populate_exact(); +}; + +std::ostream &operator<<(std::ostream &os, const Plane *plane); + +/** + * A #Face has a sequence of Verts that for a CCW ordering around them. + * Faces carry an index, created at allocation time, useful for making + * pointer-independent algorithms, and for debugging. + * They also carry an original index, meaningful to the caller. + * And they carry original edge indices too: each is a number meaningful + * to the caller for the edge starting from the corresponding face position. + * A "face position" is the index of a vertex around a face. + * Faces don't own the memory pointed at by the vert array. + * Also indexed by face position, the is_intersect array says + * for each edge whether or not it is the result of intersecting + * with another face in the intersect algorithm. + * Since the intersect algorithm needs the plane for each face, + * a #Face also stores the Plane of the face, but this is only + * populate later because not all faces will be intersected. + */ +struct Face : NonCopyable { + Array<const Vert *> vert; + Array<int> edge_orig; + Array<bool> is_intersect; + Plane *plane = nullptr; + int id = NO_INDEX; + int orig = NO_INDEX; + + using FacePos = int; + + Face() = default; + Face(Span<const Vert *> verts, int id, int orig, Span<int> edge_origs, Span<bool> is_intersect); + Face(Span<const Vert *> verts, int id, int orig); + ~Face(); + + bool is_tri() const + { + return vert.size() == 3; + } + + /* Test equality of verts, in same positions. */ + bool operator==(const Face &other) const; + + /* Test equaliy faces allowing cyclic shifts. */ + bool cyclic_equal(const Face &other) const; + + FacePos next_pos(FacePos p) const + { + return (p + 1) % vert.size(); + } + + FacePos prev_pos(FacePos p) const + { + return (p + vert.size() - 1) % vert.size(); + } + + const Vert *const &operator[](int index) const + { + return vert[index]; + } + + int size() const + { + return vert.size(); + } + + const Vert *const *begin() const + { + return vert.begin(); + } + + const Vert *const *end() const + { + return vert.end(); + } + + IndexRange index_range() const + { + return IndexRange(vert.size()); + } + + void populate_plane(bool need_exact); + + bool plane_populated() const + { + return plane != nullptr; + } +}; + +std::ostream &operator<<(std::ostream &os, const Face *f); + +/** + * #IMeshArena is the owner of the Vert and Face resources used + * during a run of one of the mesh-intersect main functions. + * It also keeps has a hash table of all Verts created so that it can + * ensure that only one instance of a Vert with a given co_exact will + * exist. I.e., it de-duplicates the vertices. + */ +class IMeshArena : NonCopyable, NonMovable { + class IMeshArenaImpl; + std::unique_ptr<IMeshArenaImpl> pimpl_; + + public: + IMeshArena(); + ~IMeshArena(); + + /** + * Provide hints to number of expected Verts and Faces expected + * to be allocated. + */ + void reserve(int vert_num_hint, int face_num_hint); + + int tot_allocated_verts() const; + int tot_allocated_faces() const; + + /** + * These add routines find and return an existing Vert with the same + * co_exact, if it exists (the orig argument is ignored in this case), + * or else allocates and returns a new one. The index field of a + * newly allocated Vert will be the index in creation order. + */ + const Vert *add_or_find_vert(const mpq3 &co, int orig); + const Vert *add_or_find_vert(const double3 &co, int orig); + + Face *add_face(Span<const Vert *> verts, + int orig, + Span<int> edge_origs, + Span<bool> is_intersect); + Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs); + Face *add_face(Span<const Vert *> verts, int orig); + + /** The following return #nullptr if not found. */ + const Vert *find_vert(const mpq3 &co) const; + const Face *find_face(Span<const Vert *> verts) const; +}; + +/** + * A #blender::meshintersect::IMesh is a self-contained mesh structure + * that can be used in `blenlib` without depending on the rest of Blender. + * The Vert and #Face resources used in the #IMesh should be owned by + * some #IMeshArena. + * The Verts used by a #IMesh can be recovered from the Faces, so + * are usually not stored, but on request, the #IMesh can populate + * internal structures for indexing exactly the set of needed Verts, + * and also going from a Vert pointer to the index in that system. + */ + +class IMesh { + Array<Face *> face_; /* Not `const` so can lazily populate planes. */ + Array<const Vert *> vert_; /* Only valid if vert_populated_. */ + Map<const Vert *, int> vert_to_index_; /* Only valid if vert_populated_. */ + bool vert_populated_ = false; + + public: + IMesh() = default; + IMesh(Span<Face *> faces) : face_(faces) + { + } + + void set_faces(Span<Face *> faces); + Face *face(int index) const + { + return face_[index]; + } + + int face_size() const + { + return face_.size(); + } + + int vert_size() const + { + return vert_.size(); + } + + bool has_verts() const + { + return vert_populated_; + } + + void set_dirty_verts() + { + vert_populated_ = false; + vert_to_index_.clear(); + vert_ = Array<const Vert *>(); + } + + /* Pass `max_verts` if there is a good bound estimate on the maximum number of verts. */ + void populate_vert(); + void populate_vert(int max_verts); + + const Vert *vert(int index) const + { + BLI_assert(vert_populated_); + return vert_[index]; + } + + /** Returns index in vert_ where v is, or #NO_INDEX. */ + int lookup_vert(const Vert *v) const; + + IndexRange vert_index_range() const + { + BLI_assert(vert_populated_); + return IndexRange(vert_.size()); + } + + IndexRange face_index_range() const + { + return IndexRange(face_.size()); + } + + Span<const Vert *> vertices() const + { + BLI_assert(vert_populated_); + return Span<const Vert *>(vert_); + } + + Span<Face *> faces() const + { + return Span<Face *>(face_); + } + + /** + * Replace face at given index with one that elides the + * vertices at the positions in face_pos_erase that are true. + * Use arena to allocate the new face in. + */ + void erase_face_positions(int f_index, Span<bool> face_pos_erase, IMeshArena *arena); +}; + +std::ostream &operator<<(std::ostream &os, const IMesh &mesh); + +/** + * The output will have duplicate vertices merged and degenerate triangles ignored. + * If the input has overlapping co-planar triangles, then there will be + * as many duplicates as there are overlaps in each overlapping triangular region. + * The orig field of each #IndexedTriangle will give the orig index in the input #IMesh + * that the output triangle was a part of (input can have -1 for that field and then + * the index in `tri[]` will be used as the original index). + * The orig structure of the output #IMesh gives the originals for vertices and edges. + * Note: if the input tm_in has a non-empty orig structure, then it is ignored. + */ +IMesh trimesh_self_intersect(const IMesh &tm_in, IMeshArena *arena); + +IMesh trimesh_nary_intersect(const IMesh &tm_in, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMeshArena *arena); + +/** This has the side effect of populating verts in the #IMesh. */ +void write_obj_mesh(IMesh &m, const std::string &objname); + +} /* namespace blender::meshintersect */ + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_mpq2.hh b/source/blender/blenlib/BLI_mpq2.hh new file mode 100644 index 00000000000..6261b50466b --- /dev/null +++ b/source/blender/blenlib/BLI_mpq2.hh @@ -0,0 +1,184 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef WITH_GMP + +# include "BLI_math_mpq.hh" +# include "BLI_mpq3.hh" + +namespace blender { + +struct mpq2 { + mpq_class x, y; + + mpq2() = default; + + mpq2(const mpq_class *ptr) : x{ptr[0]}, y{ptr[1]} + { + } + + mpq2(mpq_class x, mpq_class y) : x(x), y(y) + { + } + + mpq2(const mpq2 &other) : x(other.x), y(other.y) + { + } + + mpq2(mpq2 &&other) noexcept : x(std::move(other.x)), y(std::move(other.y)) + { + } + + ~mpq2() = default; + + mpq2 &operator=(const mpq2 &other) + { + if (this != &other) { + x = other.x; + y = other.y; + } + return *this; + } + + mpq2 &operator=(mpq2 &&other) noexcept + { + x = std::move(other.x); + y = std::move(other.y); + return *this; + } + + mpq2(const mpq3 &other) : x(other.x), y(other.y) + { + } + + operator mpq_class *() + { + return &x; + } + + operator const mpq_class *() const + { + return &x; + } + + /** + * Cannot do this exactly in rational arithmetic! + * Approximate by going in and out of doubles. + */ + mpq_class length() const + { + mpq_class lsquared = dot(*this, *this); + return mpq_class(sqrt(lsquared.get_d())); + } + + friend mpq2 operator+(const mpq2 &a, const mpq2 &b) + { + return {a.x + b.x, a.y + b.y}; + } + + friend mpq2 operator-(const mpq2 &a, const mpq2 &b) + { + return {a.x - b.x, a.y - b.y}; + } + + friend mpq2 operator*(const mpq2 &a, mpq_class b) + { + return {a.x * b, a.y * b}; + } + + friend mpq2 operator/(const mpq2 &a, mpq_class b) + { + BLI_assert(b != 0); + return {a.x / b, a.y / b}; + } + + friend mpq2 operator*(mpq_class a, const mpq2 &b) + { + return b * a; + } + + friend bool operator==(const mpq2 &a, const mpq2 &b) + { + return a.x == b.x && a.y == b.y; + } + + friend bool operator!=(const mpq2 &a, const mpq2 &b) + { + return a.x != b.x || a.y != b.y; + } + + friend std::ostream &operator<<(std::ostream &stream, const mpq2 &v) + { + stream << "(" << v.x << ", " << v.y << ")"; + return stream; + } + + static mpq_class dot(const mpq2 &a, const mpq2 &b) + { + return a.x * b.x + a.y * b.y; + } + + static mpq2 interpolate(const mpq2 &a, const mpq2 &b, mpq_class t) + { + return a * (1 - t) + b * t; + } + + static mpq2 abs(const mpq2 &a) + { + mpq_class abs_x = (a.x >= 0) ? a.x : -a.x; + mpq_class abs_y = (a.y >= 0) ? a.y : -a.y; + return mpq2(abs_x, abs_y); + } + + static mpq_class distance(const mpq2 &a, const mpq2 &b) + { + return (a - b).length(); + } + + static mpq_class distance_squared(const mpq2 &a, const mpq2 &b) + { + return dot(a, b); + } + + struct isect_result { + enum { + LINE_LINE_COLINEAR = -1, + LINE_LINE_NONE = 0, + LINE_LINE_EXACT = 1, + LINE_LINE_CROSS = 2, + } kind; + mpq_class lambda; + mpq_class mu; + }; + + static isect_result isect_seg_seg(const mpq2 &v1, + const mpq2 &v2, + const mpq2 &v3, + const mpq2 &v4); + + /** There is a sensible use for hashing on exact arithmetic types. */ + uint64_t hash() const; +}; + +} // namespace blender + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_mpq3.hh b/source/blender/blenlib/BLI_mpq3.hh new file mode 100644 index 00000000000..fb5e2b61cdb --- /dev/null +++ b/source/blender/blenlib/BLI_mpq3.hh @@ -0,0 +1,281 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + */ + +#ifdef WITH_GMP + +# include <iostream> + +# include "BLI_math.h" +# include "BLI_math_mpq.hh" +# include "BLI_span.hh" + +namespace blender { + +struct mpq3 { + mpq_class x, y, z; + + mpq3() = default; + + mpq3(const mpq_class *ptr) : x{ptr[0]}, y{ptr[1]}, z{ptr[2]} + { + } + + mpq3(const mpq_class (*ptr)[3]) : mpq3((const mpq_class *)ptr) + { + } + + explicit mpq3(mpq_class value) : x(value), y(value), z(value) + { + } + + explicit mpq3(int value) : x(value), y(value), z(value) + { + } + + mpq3(mpq_class x, mpq_class y, mpq_class z) : x{x}, y{y}, z{z} + { + } + + operator const mpq_class *() const + { + return &x; + } + + operator mpq_class *() + { + return &x; + } + + /* Cannot do this exactly in rational arithmetic! + * Approximate by going in and out of doubles. + */ + mpq_class normalize_and_get_length() + { + double dv[3] = {x.get_d(), y.get_d(), z.get_d()}; + double len = normalize_v3_db(dv); + this->x = mpq_class(dv[0]); + this->y = mpq_class(dv[1]); + this->z = mpq_class(dv[2]); + return len; + } + + mpq3 normalized() const + { + double dv[3] = {x.get_d(), y.get_d(), z.get_d()}; + double dr[3]; + normalize_v3_v3_db(dr, dv); + return mpq3(mpq_class(dr[0]), mpq_class(dr[1]), mpq_class(dr[2])); + } + + /* Cannot do this exactly in rational arithmetic! + * Approximate by going in and out of double. + */ + mpq_class length() const + { + mpq_class lsquared = this->length_squared(); + double dsquared = lsquared.get_d(); + double d = sqrt(dsquared); + return mpq_class(d); + } + + mpq_class length_squared() const + { + return x * x + y * y + z * z; + } + + void reflect(const mpq3 &normal) + { + *this = this->reflected(normal); + } + + mpq3 reflected(const mpq3 &normal) const + { + mpq3 result; + const mpq_class dot2 = 2 * dot(*this, normal); + result.x = this->x - (dot2 * normal.x); + result.y = this->y - (dot2 * normal.y); + result.z = this->z - (dot2 * normal.z); + return result; + } + + static mpq3 safe_divide(const mpq3 &a, const mpq3 &b) + { + mpq3 result; + result.x = (b.x == 0) ? mpq_class(0) : a.x / b.x; + result.y = (b.y == 0) ? mpq_class(0) : a.y / b.y; + result.z = (b.z == 0) ? mpq_class(0) : a.z / b.z; + return result; + } + + void invert() + { + x = -x; + y = -y; + z = -z; + } + + friend mpq3 operator+(const mpq3 &a, const mpq3 &b) + { + return mpq3(a.x + b.x, a.y + b.y, a.z + b.z); + } + + void operator+=(const mpq3 &b) + { + this->x += b.x; + this->y += b.y; + this->z += b.z; + } + + friend mpq3 operator-(const mpq3 &a, const mpq3 &b) + { + return mpq3(a.x - b.x, a.y - b.y, a.z - b.z); + } + + friend mpq3 operator-(const mpq3 &a) + { + return mpq3(-a.x, -a.y, -a.z); + } + + void operator-=(const mpq3 &b) + { + this->x -= b.x; + this->y -= b.y; + this->z -= b.z; + } + + void operator*=(mpq_class scalar) + { + this->x *= scalar; + this->y *= scalar; + this->z *= scalar; + } + + void operator*=(const mpq3 &other) + { + this->x *= other.x; + this->y *= other.y; + this->z *= other.z; + } + + friend mpq3 operator*(const mpq3 &a, const mpq3 &b) + { + return {a.x * b.x, a.y * b.y, a.z * b.z}; + } + + friend mpq3 operator*(const mpq3 &a, const mpq_class &b) + { + return mpq3(a.x * b, a.y * b, a.z * b); + } + + friend mpq3 operator*(const mpq_class &a, const mpq3 &b) + { + return mpq3(a * b.x, a * b.y, a * b.z); + } + + friend mpq3 operator/(const mpq3 &a, const mpq_class &b) + { + BLI_assert(b != 0); + return mpq3(a.x / b, a.y / b, a.z / b); + } + + friend bool operator==(const mpq3 &a, const mpq3 &b) + { + return a.x == b.x && a.y == b.y && a.z == b.z; + } + + friend bool operator!=(const mpq3 &a, const mpq3 &b) + { + return a.x != b.x || a.y != b.y || a.z != b.z; + } + + friend std::ostream &operator<<(std::ostream &stream, const mpq3 &v) + { + stream << "(" << v.x << ", " << v.y << ", " << v.z << ")"; + return stream; + } + + static mpq_class dot(const mpq3 &a, const mpq3 &b) + { + return a.x * b.x + a.y * b.y + a.z * b.z; + } + + static mpq3 cross(const mpq3 &a, const mpq3 &b) + { + return mpq3(a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]); + } + + static mpq3 cross_high_precision(const mpq3 &a, const mpq3 &b) + { + return cross(a, b); + } + + static mpq3 project(const mpq3 &a, const mpq3 &b) + { + const mpq_class mul = mpq3::dot(a, b) / mpq3::dot(b, b); + return mpq3(mul * b[0], mul * b[1], mul * b[2]); + } + + static mpq_class distance(const mpq3 &a, const mpq3 &b) + { + mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z); + return diff.length(); + } + + static mpq_class distance_squared(const mpq3 &a, const mpq3 &b) + { + mpq3 diff(a.x - b.x, a.y - b.y, a.z - b.z); + return mpq3::dot(diff, diff); + } + + static mpq3 interpolate(const mpq3 &a, const mpq3 &b, mpq_class t) + { + mpq_class s = 1 - t; + return mpq3(a.x * s + b.x * t, a.y * s + b.y * t, a.z * s + b.z * t); + } + + static mpq3 abs(const mpq3 &a) + { + mpq_class abs_x = (a.x >= 0) ? a.x : -a.x; + mpq_class abs_y = (a.y >= 0) ? a.y : -a.y; + mpq_class abs_z = (a.z >= 0) ? a.z : -a.z; + return mpq3(abs_x, abs_y, abs_z); + } + + static int dominant_axis(const mpq3 &a) + { + mpq_class x = (a.x >= 0) ? a.x : -a.x; + mpq_class y = (a.y >= 0) ? a.y : -a.y; + mpq_class z = (a.z >= 0) ? a.z : -a.z; + return ((x > y) ? ((x > z) ? 0 : 2) : ((y > z) ? 1 : 2)); + } + + static mpq3 cross_poly(Span<mpq3> poly); + + /** There is a sensible use for hashing on exact arithmetic types. */ + uint64_t hash() const; +}; + +uint64_t hash_mpq_class(const mpq_class &value); + +} // namespace blender + +#endif /* WITH_GMP */ diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index 477a03cf623..9684f372db7 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -170,62 +170,68 @@ class Set { * is. This is necessary to avoid a high cost when no elements are added at all. An optimized * grow operation is performed on the first insertion. */ - Set() + Set(Allocator allocator = {}) noexcept : removed_slots_(0), occupied_and_removed_slots_(0), usable_slots_(0), slot_mask_(0), - slots_(1) + slots_(1, allocator) { } - ~Set() = default; + Set(NoExceptConstructor, Allocator allocator = {}) noexcept : Set(allocator) + { + } + + Set(Span<Key> values, Allocator allocator = {}) : Set(NoExceptConstructor(), allocator) + { + this->add_multiple(values); + } /** * Construct a set that contains the given keys. Duplicates will be removed automatically. */ - Set(const std::initializer_list<Key> &list) : Set() + Set(const std::initializer_list<Key> &values) : Set(Span<Key>(values)) { - this->add_multiple(list); } + ~Set() = default; + Set(const Set &other) = default; - Set(Set &&other) noexcept - : removed_slots_(other.removed_slots_), - occupied_and_removed_slots_(other.occupied_and_removed_slots_), - usable_slots_(other.usable_slots_), - slot_mask_(other.slot_mask_), - hash_(std::move(other.hash_)), - is_equal_(std::move(other.is_equal_)), - slots_(std::move(other.slots_)) + Set(Set &&other) noexcept(std::is_nothrow_move_constructible_v<SlotArray>) + : Set(NoExceptConstructor(), other.slots_.allocator()) + { - other.~Set(); - new (&other) Set(); + if constexpr (std::is_nothrow_move_constructible_v<SlotArray>) { + slots_ = std::move(other.slots_); + } + else { + try { + slots_ = std::move(other.slots_); + } + catch (...) { + other.noexcept_reset(); + throw; + } + } + removed_slots_ = other.removed_slots_; + occupied_and_removed_slots_ = other.occupied_and_removed_slots_; + usable_slots_ = other.usable_slots_; + slot_mask_ = other.slot_mask_; + hash_ = std::move(other.hash_); + is_equal_ = std::move(other.is_equal_); + other.noexcept_reset(); } Set &operator=(const Set &other) { - if (this == &other) { - return *this; - } - - this->~Set(); - new (this) Set(other); - - return *this; + return copy_assign_container(*this, other); } Set &operator=(Set &&other) { - if (this == &other) { - return *this; - } - - this->~Set(); - new (this) Set(std::move(other)); - - return *this; + return move_assign_container(*this, std::move(other)); } /** @@ -562,8 +568,13 @@ class Set { * Optimize the case when the set was empty beforehand. We can avoid some copies here. */ if (this->size() == 0) { - slots_.~Array(); - new (&slots_) SlotArray(total_slots); + try { + slots_.reinitialize(total_slots); + } + catch (...) { + this->noexcept_reset(); + throw; + } removed_slots_ = 0; occupied_and_removed_slots_ = 0; usable_slots_ = usable_slots; @@ -574,38 +585,51 @@ class Set { /* The grown array that we insert the keys into. */ SlotArray new_slots(total_slots); - for (Slot &slot : slots_) { - if (slot.is_occupied()) { - this->add_after_grow_and_destruct_old(slot, new_slots, new_slot_mask); + try { + for (Slot &slot : slots_) { + if (slot.is_occupied()) { + this->add_after_grow(slot, new_slots, new_slot_mask); + slot.remove(); + } } + slots_ = std::move(new_slots); + } + catch (...) { + this->noexcept_reset(); + throw; } - /* All occupied slots have been destructed already and empty/removed slots are assumed to be - * trivially destructible. */ - slots_.clear_without_destruct(); - slots_ = std::move(new_slots); occupied_and_removed_slots_ -= removed_slots_; usable_slots_ = usable_slots; removed_slots_ = 0; slot_mask_ = new_slot_mask; } - void add_after_grow_and_destruct_old(Slot &old_slot, - SlotArray &new_slots, - const uint64_t new_slot_mask) + void add_after_grow(Slot &old_slot, SlotArray &new_slots, const uint64_t new_slot_mask) { const uint64_t hash = old_slot.get_hash(Hash()); SLOT_PROBING_BEGIN (ProbingStrategy, hash, new_slot_mask, slot_index) { Slot &slot = new_slots[slot_index]; if (slot.is_empty()) { - slot.relocate_occupied_here(old_slot, hash); + slot.occupy(std::move(*old_slot.key()), hash); return; } } SLOT_PROBING_END(); } + /** + * In some cases when exceptions are thrown, it's best to just reset the entire container to make + * sure that invariants are maintained. This should happen very rarely in practice. + */ + void noexcept_reset() noexcept + { + Allocator allocator = slots_.allocator(); + this->~Set(); + new (this) Set(NoExceptConstructor(), allocator); + } + template<typename ForwardKey> bool contains__impl(const ForwardKey &key, const uint64_t hash) const { @@ -699,11 +723,11 @@ class Set { void remove_contained__impl(const ForwardKey &key, const uint64_t hash) { BLI_assert(this->contains_as(key)); - removed_slots_++; SET_SLOT_PROBING_BEGIN (hash, slot) { if (slot.contains(key, is_equal_, hash)) { slot.remove(); + removed_slots_++; return; } } diff --git a/source/blender/blenlib/BLI_set_slots.hh b/source/blender/blenlib/BLI_set_slots.hh index ee5da17fcaf..a4d01dfdb68 100644 --- a/source/blender/blenlib/BLI_set_slots.hh +++ b/source/blender/blenlib/BLI_set_slots.hh @@ -88,7 +88,7 @@ template<typename Key> class SimpleSetSlot { * other slot has to be moved as well. The other slot stays in the state it was in before. Its * optionally stored key remains in a moved-from state. */ - SimpleSetSlot(SimpleSetSlot &&other) noexcept + SimpleSetSlot(SimpleSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>) { state_ = other.state_; if (other.state_ == Occupied) { @@ -139,19 +139,6 @@ template<typename Key> class SimpleSetSlot { } /** - * Move the other slot into this slot and destruct it. We do destruction here, because this way - * we can avoid a comparison with the state, since we know the slot is occupied. - */ - void relocate_occupied_here(SimpleSetSlot &other, uint64_t UNUSED(hash)) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - state_ = Occupied; - new (&key_buffer_) Key(std::move(*other.key_buffer_)); - other.key_buffer_.ref().~Key(); - } - - /** * Return true, when this slot is occupied and contains a key that compares equal to the given * key. The hash is used by other slot implementations to determine inequality faster. */ @@ -171,8 +158,8 @@ template<typename Key> class SimpleSetSlot { template<typename ForwardKey> void occupy(ForwardKey &&key, uint64_t UNUSED(hash)) { BLI_assert(!this->is_occupied()); - state_ = Occupied; new (&key_buffer_) Key(std::forward<ForwardKey>(key)); + state_ = Occupied; } /** @@ -181,8 +168,8 @@ template<typename Key> class SimpleSetSlot { void remove() { BLI_assert(this->is_occupied()); - state_ = Removed; key_buffer_.ref().~Key(); + state_ = Removed; } }; @@ -224,7 +211,7 @@ template<typename Key> class HashedSetSlot { } } - HashedSetSlot(HashedSetSlot &&other) noexcept + HashedSetSlot(HashedSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>) { state_ = other.state_; if (other.state_ == Occupied) { @@ -259,16 +246,6 @@ template<typename Key> class HashedSetSlot { return hash_; } - void relocate_occupied_here(HashedSetSlot &other, const uint64_t hash) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - state_ = Occupied; - hash_ = hash; - new (&key_buffer_) Key(std::move(*other.key_buffer_)); - key_buffer_.ref().~Key(); - } - template<typename ForwardKey, typename IsEqual> bool contains(const ForwardKey &key, const IsEqual &is_equal, const uint64_t hash) const { @@ -284,16 +261,16 @@ template<typename Key> class HashedSetSlot { template<typename ForwardKey> void occupy(ForwardKey &&key, const uint64_t hash) { BLI_assert(!this->is_occupied()); + new (&key_buffer_) Key(std::forward<ForwardKey>(key)); state_ = Occupied; hash_ = hash; - new (&key_buffer_) Key(std::forward<ForwardKey>(key)); } void remove() { BLI_assert(this->is_occupied()); - state_ = Removed; key_buffer_.ref().~Key(); + state_ = Removed; } }; @@ -313,7 +290,8 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot { IntrusiveSetSlot() = default; ~IntrusiveSetSlot() = default; IntrusiveSetSlot(const IntrusiveSetSlot &other) = default; - IntrusiveSetSlot(IntrusiveSetSlot &&other) noexcept = default; + IntrusiveSetSlot(IntrusiveSetSlot &&other) noexcept(std::is_nothrow_move_constructible_v<Key>) = + default; Key *key() { @@ -341,14 +319,6 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot { return hash(key_); } - void relocate_occupied_here(IntrusiveSetSlot &other, const uint64_t UNUSED(hash)) - { - BLI_assert(!this->is_occupied()); - BLI_assert(other.is_occupied()); - key_ = std::move(other.key_); - other.key_.~Key(); - } - template<typename ForwardKey, typename IsEqual> bool contains(const ForwardKey &key, const IsEqual &is_equal, const uint64_t UNUSED(hash)) const { @@ -360,7 +330,6 @@ template<typename Key, typename KeyInfo> class IntrusiveSetSlot { { BLI_assert(!this->is_occupied()); BLI_assert(KeyInfo::is_not_empty_or_removed(key)); - key_ = std::forward<ForwardKey>(key); } diff --git a/source/blender/blenlib/BLI_stack.hh b/source/blender/blenlib/BLI_stack.hh index 8eca356ec54..a463ac102f1 100644 --- a/source/blender/blenlib/BLI_stack.hh +++ b/source/blender/blenlib/BLI_stack.hh @@ -117,7 +117,7 @@ class Stack { /** * Initialize an empty stack. No heap allocation is done. */ - Stack(Allocator allocator = {}) : allocator_(allocator) + Stack(Allocator allocator = {}) noexcept : allocator_(allocator) { inline_chunk_.below = nullptr; inline_chunk_.above = nullptr; @@ -129,11 +129,15 @@ class Stack { size_ = 0; } + Stack(NoExceptConstructor, Allocator allocator = {}) noexcept : Stack(allocator) + { + } + /** * Create a new stack that contains the given elements. The values are pushed to the stack in * the order they are in the array. */ - Stack(Span<T> values) : Stack() + Stack(Span<T> values, Allocator allocator = {}) : Stack(NoExceptConstructor(), allocator) { this->push_multiple(values); } @@ -147,11 +151,12 @@ class Stack { * assert(stack.pop() == 6); * assert(stack.pop() == 5); */ - Stack(const std::initializer_list<T> &values) : Stack(Span<T>(values)) + Stack(const std::initializer_list<T> &values, Allocator allocator = {}) + : Stack(Span<T>(values), allocator) { } - Stack(const Stack &other) : Stack(other.allocator_) + Stack(const Stack &other) : Stack(NoExceptConstructor(), other.allocator_) { for (const Chunk *chunk = &other.inline_chunk_; chunk; chunk = chunk->above) { const T *begin = chunk->begin; @@ -160,7 +165,8 @@ class Stack { } } - Stack(Stack &&other) noexcept : Stack(other.allocator_) + Stack(Stack &&other) noexcept(std::is_nothrow_move_constructible_v<T>) + : Stack(NoExceptConstructor(), other.allocator_) { uninitialized_relocate_n<T>( other.inline_buffer_, std::min(other.size_, InlineBufferCapacity), inline_buffer_); @@ -197,28 +203,14 @@ class Stack { } } - Stack &operator=(const Stack &stack) + Stack &operator=(const Stack &other) { - if (this == &stack) { - return *this; - } - - this->~Stack(); - new (this) Stack(stack); - - return *this; + return copy_assign_container(*this, other); } - Stack &operator=(Stack &&stack) + Stack &operator=(Stack &&other) { - if (this == &stack) { - return *this; - } - - this->~Stack(); - new (this) Stack(std::move(stack)); - - return *this; + return move_assign_container(*this, std::move(other)); } /** @@ -226,21 +218,26 @@ class Stack { */ void push(const T &value) { - if (top_ == top_chunk_->capacity_end) { - this->activate_next_chunk(1); - } - new (top_) T(value); - top_++; - size_++; + this->push_as(value); } void push(T &&value) { + this->push_as(std::move(value)); + } + template<typename ForwardT> void push_as(ForwardT &&value) + { if (top_ == top_chunk_->capacity_end) { this->activate_next_chunk(1); } - new (top_) T(std::move(value)); - top_++; - size_++; + try { + new (top_) T(std::forward<ForwardT>(value)); + top_++; + size_++; + } + catch (...) { + this->move_top_pointer_back_to_below_chunk(); + throw; + } } /** @@ -250,8 +247,8 @@ class Stack { T pop() { BLI_assert(size_ > 0); + T value = std::move(*(top_ - 1)); top_--; - T value = std::move(*top_); top_->~T(); size_--; @@ -296,13 +293,18 @@ class Stack { const int64_t remaining_capacity = top_chunk_->capacity_end - top_; const int64_t amount = std::min(remaining_values.size(), remaining_capacity); - uninitialized_copy_n(remaining_values.data(), amount, top_); + try { + uninitialized_copy_n(remaining_values.data(), amount, top_); + } + catch (...) { + this->move_top_pointer_back_to_below_chunk(); + throw; + } top_ += amount; + size_ += amount; remaining_values = remaining_values.drop_front(amount); } - - size_ += values.size(); } /** @@ -332,6 +334,15 @@ class Stack { top_ = top_chunk_->begin; } + /* This should only be called by unit tests. */ + bool is_invariant_maintained() const + { + if (size_ == 0) { + return top_ == inline_chunk_.begin; + } + return top_ > top_chunk_->begin; + } + private: /** * Changes top_chunk_ to point to a new chunk that is above the current one. The new chunk might @@ -365,6 +376,18 @@ class Stack { top_ = top_chunk_->begin; } + void move_top_pointer_back_to_below_chunk() + { + /* This makes sure that the invariant stays intact after a failed push. */ + if (size_ == 0) { + top_ = inline_chunk_.begin; + } + else if (top_ == top_chunk_->begin) { + top_chunk_ = top_chunk_->below; + top_ = top_chunk_->capacity_end; + } + } + void destruct_all_elements() { for (T *value = top_chunk_->begin; value != top_; value++) { diff --git a/source/blender/blenlib/BLI_vector.hh b/source/blender/blenlib/BLI_vector.hh index 74ce8dd42e7..3c90e1ab755 100644 --- a/source/blender/blenlib/BLI_vector.hh +++ b/source/blender/blenlib/BLI_vector.hh @@ -118,7 +118,7 @@ class Vector { * Create an empty vector. * This does not do any memory allocation. */ - Vector(Allocator allocator = {}) : allocator_(allocator) + Vector(Allocator allocator = {}) noexcept : allocator_(allocator) { begin_ = inline_buffer_; end_ = begin_; @@ -126,12 +126,17 @@ class Vector { UPDATE_VECTOR_SIZE(this); } + Vector(NoExceptConstructor, Allocator allocator = {}) noexcept : Vector(allocator) + { + } + /** * Create a vector with a specific size. * The elements will be default constructed. * If T is trivially constructible, the elements in the vector are not touched. */ - explicit Vector(int64_t size) : Vector() + explicit Vector(int64_t size, Allocator allocator = {}) + : Vector(NoExceptConstructor(), allocator) { this->resize(size); } @@ -139,7 +144,8 @@ class Vector { /** * Create a vector filled with a specific value. */ - Vector(int64_t size, const T &value) : Vector() + Vector(int64_t size, const T &value, Allocator allocator = {}) + : Vector(NoExceptConstructor(), allocator) { this->resize(size, value); } @@ -148,12 +154,12 @@ class Vector { * Create a vector from an array ref. The values in the vector are copy constructed. */ template<typename U, typename std::enable_if_t<std::is_convertible_v<U, T>> * = nullptr> - Vector(Span<U> values, Allocator allocator = {}) : Vector(allocator) + Vector(Span<U> values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator) { const int64_t size = values.size(); this->reserve(size); - this->increase_size_by_unchecked(size); uninitialized_convert_n<U, T>(values.data(), size, begin_); + this->increase_size_by_unchecked(size); } /** @@ -182,7 +188,8 @@ class Vector { /* This constructor should not be called with e.g. Vector(3, 10), because that is expected to produce the vector (10, 10, 10). */ typename std::enable_if_t<!std::is_convertible_v<InputIt, int>> * = nullptr> - Vector(InputIt first, InputIt last, Allocator allocator = {}) : Vector(std::move(allocator)) + Vector(InputIt first, InputIt last, Allocator allocator = {}) + : Vector(NoExceptConstructor(), allocator) { for (InputIt current = first; current != last; ++current) { this->append(*current); @@ -196,7 +203,7 @@ class Vector { * Example Usage: * Vector<ModifierData *> modifiers(ob->modifiers); */ - Vector(ListBase &values) : Vector() + Vector(ListBase &values, Allocator allocator = {}) : Vector(NoExceptConstructor(), allocator) { LISTBASE_FOREACH (T, value, &values) { this->append(value); @@ -226,27 +233,26 @@ class Vector { * have zero elements afterwards. */ template<int64_t OtherInlineBufferCapacity> - Vector(Vector<T, OtherInlineBufferCapacity, Allocator> &&other) noexcept - : allocator_(other.allocator_) + Vector(Vector<T, OtherInlineBufferCapacity, Allocator> &&other) noexcept( + std::is_nothrow_move_constructible_v<T>) + : Vector(NoExceptConstructor(), other.allocator_) { const int64_t size = other.size(); if (other.is_inline()) { if (size <= InlineBufferCapacity) { /* Copy between inline buffers. */ - begin_ = inline_buffer_; - end_ = begin_ + size; - capacity_end_ = begin_ + InlineBufferCapacity; uninitialized_relocate_n(other.begin_, size, begin_); + end_ = begin_ + size; } else { /* Copy from inline buffer to newly allocated buffer. */ const int64_t capacity = size; begin_ = static_cast<T *>( allocator_.allocate(sizeof(T) * static_cast<size_t>(capacity), alignof(T), AT)); - end_ = begin_ + size; capacity_end_ = begin_ + capacity; uninitialized_relocate_n(other.begin_, size, begin_); + end_ = begin_ + size; } } else { @@ -273,28 +279,12 @@ class Vector { Vector &operator=(const Vector &other) { - if (this == &other) { - return *this; - } - - this->~Vector(); - new (this) Vector(other); - - return *this; + return copy_assign_container(*this, other); } Vector &operator=(Vector &&other) { - if (this == &other) { - return *this; - } - - /* This can be incorrect, when the vector is used to build a recursive data structure. However, - we don't take care of it at this low level. See https://youtu.be/7Qgd9B1KuMQ?t=840. */ - this->~Vector(); - new (this) Vector(std::move(other)); - - return *this; + return move_assign_container(*this, std::move(other)); } /** @@ -474,17 +464,10 @@ class Vector { * behavior when not enough capacity has been reserved beforehand. Only use this in performance * critical code. */ - void append_unchecked(const T &value) - { - BLI_assert(end_ < capacity_end_); - new (end_) T(value); - end_++; - UPDATE_VECTOR_SIZE(this); - } - void append_unchecked(T &&value) + template<typename ForwardT> void append_unchecked(ForwardT &&value) { BLI_assert(end_ < capacity_end_); - new (end_) T(std::move(value)); + new (end_) T(std::forward<ForwardT>(value)); end_++; UPDATE_VECTOR_SIZE(this); } @@ -497,7 +480,7 @@ class Vector { { BLI_assert(n >= 0); this->reserve(this->size() + n); - blender::uninitialized_fill_n(end_, n, value); + uninitialized_fill_n(end_, n, value); this->increase_size_by_unchecked(n); } @@ -507,7 +490,7 @@ class Vector { * useful when you want to call constructors in the vector yourself. This should only be done in * very rare cases and has to be justified every time. */ - void increase_size_by_unchecked(const int64_t n) + void increase_size_by_unchecked(const int64_t n) noexcept { BLI_assert(end_ + n <= capacity_end_); end_ += n; @@ -553,7 +536,7 @@ class Vector { { BLI_assert(amount >= 0); BLI_assert(begin_ + amount <= capacity_end_); - blender::uninitialized_copy_n(start, amount, end_); + uninitialized_copy_n(start, amount, end_); end_ += amount; UPDATE_VECTOR_SIZE(this); } @@ -600,11 +583,29 @@ class Vector { for (int64_t i = 0; i < move_amount; i++) { const int64_t src_index = insert_index + move_amount - i - 1; const int64_t dst_index = new_size - i - 1; - new (static_cast<void *>(begin_ + dst_index)) T(std::move(begin_[src_index])); + try { + new (static_cast<void *>(begin_ + dst_index)) T(std::move(begin_[src_index])); + } + catch (...) { + /* Destruct all values that have been moved already. */ + destruct_n(begin_ + dst_index + 1, i); + end_ = begin_ + src_index + 1; + UPDATE_VECTOR_SIZE(this); + throw; + } begin_[src_index].~T(); } - std::uninitialized_copy_n(first, insert_amount, begin_ + insert_index); + try { + std::uninitialized_copy_n(first, insert_amount, begin_ + insert_index); + } + catch (...) { + /* Destruct all values that have been moved. */ + destruct_n(begin_ + new_size - move_amount, move_amount); + end_ = begin_ + insert_index; + UPDATE_VECTOR_SIZE(this); + throw; + } end_ = begin_ + new_size; UPDATE_VECTOR_SIZE(this); } @@ -686,8 +687,8 @@ class Vector { T pop_last() { BLI_assert(!this->is_empty()); + T value = std::move(*(end_ - 1)); end_--; - T value = std::move(*end_); end_->~T(); UPDATE_VECTOR_SIZE(this); return value; @@ -702,10 +703,10 @@ class Vector { BLI_assert(index >= 0); BLI_assert(index < this->size()); T *element_to_remove = begin_ + index; - end_--; if (element_to_remove < end_) { - *element_to_remove = std::move(*end_); + *element_to_remove = std::move(*(end_ - 1)); } + end_--; end_->~T(); UPDATE_VECTOR_SIZE(this); } @@ -741,6 +742,27 @@ class Vector { } /** + * Remove a contiguous chunk of elements and move all values coming after it towards the front. + * This takes O(n) time. + * + * This is similar to std::vector::erase. + */ + void remove(const int64_t start_index, const int64_t amount) + { + const int64_t old_size = this->size(); + BLI_assert(start_index >= 0); + BLI_assert(amount >= 0); + BLI_assert(start_index + amount <= old_size); + const int64_t move_amount = old_size - start_index - amount; + for (int64_t i = 0; i < move_amount; i++) { + begin_[start_index + i] = std::move(begin_[start_index + amount + i]); + } + destruct_n(end_ - amount, amount); + end_ -= amount; + UPDATE_VECTOR_SIZE(this); + } + + /** * Do a linear search to find the value in the vector. * When found, return the first index, otherwise return -1. */ @@ -901,7 +923,13 @@ class Vector { T *new_array = static_cast<T *>( allocator_.allocate(static_cast<size_t>(new_capacity) * sizeof(T), alignof(T), AT)); - uninitialized_relocate_n(begin_, size, new_array); + try { + uninitialized_relocate_n(begin_, size, new_array); + } + catch (...) { + allocator_.deallocate(new_array); + throw; + } if (!this->is_inline()) { allocator_.deallocate(begin_); diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index 2de6098f6be..8076724a214 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -28,6 +28,8 @@ # error "This include is for Windows only!" #endif +#include "BLI_sys_types.h" + #define WIN32_LEAN_AND_MEAN #include <windows.h> @@ -86,6 +88,7 @@ typedef long ssize_t; # endif #endif +/* Directory reading compatibility with UNIX. */ struct dirent { int d_ino; int d_off; @@ -99,13 +102,12 @@ typedef struct __dirstream DIR; DIR *opendir(const char *path); struct dirent *readdir(DIR *dp); int closedir(DIR *dp); - -void RegisterBlendExtension(void); -void get_default_root(char *root); -int check_file_chars(char *filename); const char *dirname(char *path); -int BLI_getInstallationDir(char *str); +/* Windows utility functions. */ +void BLI_windows_register_blend_extension(const bool background); +void BLI_windows_get_default_root_dir(char *root_dir); +int BLI_windows_get_executable_dir(char *str); #ifdef __cplusplus } diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 819c74b6946..1db45cff09a 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} ${FREETYPE_INCLUDE_DIRS} + ${GMP_INCLUDE_DIRS} ) set(SRC @@ -64,7 +65,7 @@ set(SRC intern/boxpack_2d.c intern/buffer.c intern/convexhull_2d.c - intern/delaunay_2d.c + intern/delaunay_2d.cc intern/dot_export.cc intern/dynlib.c intern/easing.c @@ -89,6 +90,7 @@ set(SRC intern/math_base_inline.c intern/math_base_safe_inline.c intern/math_bits_inline.c + intern/math_boolean.cc intern/math_color.c intern/math_color_blend_inline.c intern/math_color_inline.c @@ -99,9 +101,12 @@ set(SRC intern/math_rotation.c intern/math_solvers.c intern/math_statistics.c + intern/math_vec.cc intern/math_vector.c intern/math_vector_inline.c intern/memory_utils.c + intern/mesh_boolean.cc + intern/mesh_intersect.cc intern/noise.c intern/path_util.c intern/polyfill_2d.c @@ -170,6 +175,8 @@ set(SRC BLI_dlrbTree.h BLI_dot_export.hh BLI_dot_export_attribute_enums.hh + BLI_double2.hh + BLI_double3.hh BLI_dynlib.h BLI_dynstr.h BLI_easing.h @@ -214,11 +221,13 @@ set(SRC BLI_math_base.h BLI_math_base_safe.h BLI_math_bits.h + BLI_math_boolean.hh BLI_math_color.h BLI_math_color_blend.h BLI_math_geom.h BLI_math_inline.h BLI_math_interp.h + BLI_math_mpq.hh BLI_math_matrix.h BLI_math_rotation.h BLI_math_solvers.h @@ -230,6 +239,10 @@ set(SRC BLI_memory_utils.h BLI_memory_utils.hh BLI_mempool.h + BLI_mesh_boolean.hh + BLI_mesh_intersect.hh + BLI_mpq2.hh + BLI_mpq3.hh BLI_noise.h BLI_path_util.h BLI_polyfill_2d.h @@ -306,6 +319,18 @@ if(WITH_TBB) ) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) + + list(APPEND INC_SYS + ${GMP_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${GMP_LIBRARIES} + ) +endif() + if(WIN32) list(APPEND INC ../../../intern/utfconv @@ -374,6 +399,8 @@ if(WITH_GTESTS) tests/BLI_math_vector_test.cc tests/BLI_memiter_test.cc tests/BLI_memory_utils_test.cc + tests/BLI_mesh_boolean_test.cc + tests/BLI_mesh_intersect_test.cc tests/BLI_multi_value_map_test.cc tests/BLI_path_util_test.cc tests/BLI_polyfill_2d_test.cc @@ -390,6 +417,8 @@ if(WITH_GTESTS) tests/BLI_task_test.cc tests/BLI_vector_set_test.cc tests/BLI_vector_test.cc + + tests/BLI_exception_safety_test_utils.hh ) set(TEST_INC ../imbuf diff --git a/source/blender/blenlib/intern/delaunay_2d.c b/source/blender/blenlib/intern/delaunay_2d.c deleted file mode 100644 index baf49ddaffd..00000000000 --- a/source/blender/blenlib/intern/delaunay_2d.c +++ /dev/null @@ -1,5170 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup bli - * - * Constrained 2d Delaunay Triangulation. - */ - -#include "MEM_guardedalloc.h" - -#include "BLI_array.h" -#include "BLI_bitmap.h" -#include "BLI_linklist.h" -#include "BLI_math.h" -#include "BLI_memarena.h" -#include "BLI_mempool.h" - -#include "BLI_delaunay_2d.h" - -/* Uncomment this define to get helpful debugging functions etc. defined. */ -// #define DEBUG_CDT - -struct CDTEdge; -struct CDTFace; -struct CDTVert; - -typedef struct SymEdge { - struct SymEdge *next; /* In face, doing CCW traversal of face. */ - struct SymEdge *rot; /* CCW around vert. */ - struct CDTVert *vert; /* Vert at origin. */ - struct CDTEdge *edge; /* Undirected edge this is for. */ - struct CDTFace *face; /* Face on left side. */ -} SymEdge; - -typedef struct CDTVert { - double co[2]; /* Coordinate. */ - SymEdge *symedge; /* Some edge attached to it. */ - LinkNode *input_ids; /* List of corresponding vertex input ids. */ - int index; /* Index into array that cdt keeps. */ - int merge_to_index; /* Index of a CDTVert that this has merged to. -1 if no merge. */ - int visit_index; /* Which visit epoch has this been seen. */ -} CDTVert; - -typedef struct CDTEdge { - LinkNode *input_ids; /* List of input edge ids that this is part of. */ - SymEdge symedges[2]; /* The directed edges for this edge. */ - bool in_queue; /* Used in flipping algorithm. */ -} CDTEdge; - -typedef struct CDTFace { - SymEdge *symedge; /* A symedge in face; only used during output, so only valid then. */ - LinkNode *input_ids; /* List of input face ids that this is part of. */ - int visit_index; /* Which visit epoch has this been seen. */ - bool deleted; /* Marks this face no longer used. */ - bool in_queue; /* Used in remove_small_features algorithm. */ -} CDTFace; - -typedef struct CDT_state { - LinkNode *edges; /* List of CDTEdge pointer. */ - LinkNode *faces; /* List of CDTFace pointer. */ - CDTFace *outer_face; /* Which CDTFace is the outer face. */ - CDTVert **vert_array; /* Array of CDTVert pointer, grows. */ - int vert_array_len; /* Current length of vert_array. */ - int vert_array_len_alloc; /* Allocated length of vert_array. */ - int input_vert_tot; /* How many verts were in input (will be first in vert_array). */ - double minx; /* Used for debug drawing. */ - double miny; /* Used for debug drawing. */ - double maxx; /* Used for debug drawing. */ - double maxy; /* Used for debug drawing. */ - double margin; /* Used for debug drawing. */ - int visit_count; /* Used for visiting things without having to initialized their visit fields. */ - int face_edge_offset; /* Input edge id where we start numbering the face edges. */ - MemArena *arena; /* Most allocations are done from here, so can free all at once at end. */ - BLI_mempool *listpool; /* Allocations of ListNodes done from this pool. */ - double epsilon; /* The user-specified nearness limit. */ - double epsilon_squared; /* Square of epsilon. */ - bool output_prepared; /* Set after the mesh has been modified for output (may not be all - triangles now). */ -} CDT_state; - -#define DLNY_ARENASIZE 1 << 14 - -#ifdef DEBUG_CDT -# ifdef __GNUC__ -# define ATTU __attribute__((unused)) -# else -# define ATTU -# endif -# define F2(p) p[0], p[1] -# define F3(p) p[0], p[1], p[2] -struct CrossData; -ATTU static void dump_se(const SymEdge *se, const char *lab); -ATTU static void dump_se_short(const SymEdge *se, const char *lab); -ATTU static void dump_v(const CDTVert *v, const char *lab); -ATTU static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit); -ATTU static void dump_id_list(const LinkNode *id_list, const char *lab); -ATTU static void dump_cross_data(struct CrossData *cd, const char *lab); -ATTU static void dump_cdt(const CDT_state *cdt, const char *lab); -ATTU static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab); -ATTU static void cdt_draw(CDT_state *cdt, const char *lab); -ATTU static void cdt_draw_region( - CDT_state *cdt, const char *lab, double minx, double miny, double maxx, double maxy); - -ATTU static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab); -ATTU static void cdt_draw_edge_region( - CDT_state *cdt, int v1, int v2, double dist, const char *lab); -ATTU static void write_cdt_input_to_file(const CDT_input *inp); -ATTU static void validate_cdt(CDT_state *cdt, - bool check_all_tris, - bool check_delaunay, - bool check_visibility); -#endif - -static void exactinit(void); -static double orient2d(const double *pa, const double *pb, const double *pc); -static double incircle(const double *pa, const double *pb, const double *pc, const double *pd); - -/** Return other #SymEdge for same #CDTEdge as se. */ -BLI_INLINE SymEdge *sym(const SymEdge *se) -{ - return se->next->rot; -} - -/** Return SymEdge whose next is se. */ -BLI_INLINE SymEdge *prev(const SymEdge *se) -{ - return se->rot->next->rot; -} - -/** - * Return true if a -- b -- c are in that order, assuming they are on a straight line according to - * orient2d and we know the order is either `abc` or `bac`. - * This means `ab . ac` and `bc . ac` must both be non-negative. */ -static bool in_line(const double a[2], const double b[2], const double c[2]) -{ - double ab[2], bc[2], ac[2]; - sub_v2_v2v2_db(ab, b, a); - sub_v2_v2v2_db(bc, c, b); - sub_v2_v2v2_db(ac, c, a); - if (dot_v2v2_db(ab, ac) < 0.0) { - return false; - } - return dot_v2v2_db(bc, ac) >= 0.0; -} - -#ifndef NDEBUG -/** Is s2 reachable from s1 by next pointers with < limit hops? */ -static bool reachable(SymEdge *s1, SymEdge *s2, int limit) -{ - int count = 0; - for (SymEdge *s = s1; s && count < limit; s = s->next) { - if (s == s2) { - return true; - } - count++; - } - return false; -} -#endif - -/** Using array to store these instead of linked list so can make a random selection from them. */ -static CDTVert *add_cdtvert(CDT_state *cdt, double x, double y) -{ - CDTVert *v = BLI_memarena_alloc(cdt->arena, sizeof(*v)); - v->co[0] = x; - v->co[1] = y; - v->input_ids = NULL; - v->symedge = NULL; - if (cdt->vert_array_len == cdt->vert_array_len_alloc) { - CDTVert **old_array = cdt->vert_array; - cdt->vert_array_len_alloc *= 4; - cdt->vert_array = BLI_memarena_alloc(cdt->arena, - cdt->vert_array_len_alloc * sizeof(cdt->vert_array[0])); - memmove(cdt->vert_array, old_array, cdt->vert_array_len * sizeof(cdt->vert_array[0])); - } - BLI_assert(cdt->vert_array_len < cdt->vert_array_len_alloc); - v->index = cdt->vert_array_len; - v->merge_to_index = -1; - v->visit_index = 0; - cdt->vert_array[cdt->vert_array_len++] = v; - return v; -} - -static CDTEdge *add_cdtedge( - CDT_state *cdt, CDTVert *v1, CDTVert *v2, CDTFace *fleft, CDTFace *fright) -{ - CDTEdge *e = BLI_memarena_alloc(cdt->arena, sizeof(*e)); - SymEdge *se = &e->symedges[0]; - SymEdge *sesym = &e->symedges[1]; - e->input_ids = NULL; - e->in_queue = false; - BLI_linklist_prepend_arena(&cdt->edges, (void *)e, cdt->arena); - se->edge = sesym->edge = e; - se->face = fleft; - sesym->face = fright; - se->vert = v1; - if (v1->symedge == NULL) { - v1->symedge = se; - } - sesym->vert = v2; - if (v2->symedge == NULL) { - v2->symedge = sesym; - } - se->next = sesym->next = se->rot = sesym->rot = NULL; - return e; -} - -static CDTFace *add_cdtface(CDT_state *cdt) -{ - CDTFace *f = BLI_memarena_alloc(cdt->arena, sizeof(*f)); - f->visit_index = 0; - f->deleted = false; - f->symedge = NULL; - f->input_ids = NULL; - f->in_queue = false; - BLI_linklist_prepend_arena(&cdt->faces, (void *)f, cdt->arena); - return f; -} - -static bool id_in_list(const LinkNode *id_list, int id) -{ - const LinkNode *ln; - - for (ln = id_list; ln; ln = ln->next) { - if (POINTER_AS_INT(ln->link) == id) { - return true; - } - } - return false; -} - -/** is any id in (range_start, range_start+1, ... , range_end) in id_list? */ -static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end) -{ - const LinkNode *ln; - int id; - - for (ln = id_list; ln; ln = ln->next) { - id = POINTER_AS_INT(ln->link); - if (id >= range_start && id <= range_end) { - return true; - } - } - return false; -} - -static void add_to_input_ids(LinkNode **dst, int input_id, CDT_state *cdt) -{ - if (!id_in_list(*dst, input_id)) { - BLI_linklist_prepend_arena(dst, POINTER_FROM_INT(input_id), cdt->arena); - } -} - -static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src, CDT_state *cdt) -{ - const LinkNode *ln; - - for (ln = src; ln; ln = ln->next) { - add_to_input_ids(dst, POINTER_AS_INT(ln->link), cdt); - } -} - -BLI_INLINE bool is_border_edge(const CDTEdge *e, const CDT_state *cdt) -{ - return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face; -} - -BLI_INLINE bool is_constrained_edge(const CDTEdge *e) -{ - return e->input_ids != NULL; -} - -BLI_INLINE bool is_deleted_edge(const CDTEdge *e) -{ - return e->symedges[0].next == NULL; -} - -BLI_INLINE bool is_original_vert(const CDTVert *v, CDT_state *cdt) -{ - return (v->index < cdt->input_vert_tot); -} - -/** Return the Symedge that goes from v1 to v2, if it exists, else return NULL. */ -static SymEdge *find_symedge_between_verts(const CDTVert *v1, const CDTVert *v2) -{ - SymEdge *tstart, *t; - - t = tstart = v1->symedge; - do { - if (t->next->vert == v2) { - return t; - } - } while ((t = t->rot) != tstart); - return NULL; -} - -/** Return the SymEdge attached to v that has face f, if it exists, else return NULL. */ -static SymEdge *find_symedge_with_face(const CDTVert *v, const CDTFace *f) -{ - SymEdge *tstart, *t; - - t = tstart = v->symedge; - do { - if (t->face == f) { - return t; - } - } while ((t = t->rot) != tstart); - return NULL; -} - -/** Is there already an edge between a and b? */ -static inline bool exists_edge(const CDTVert *v1, const CDTVert *v2) -{ - return find_symedge_between_verts(v1, v2) != NULL; -} - -/** Is the vertex v incident on face f? */ -static bool vert_touches_face(const CDTVert *v, const CDTFace *f) -{ - SymEdge *se = v->symedge; - do { - if (se->face == f) { - return true; - } - } while ((se = se->rot) != v->symedge); - return false; -} - -/** - * Assume s1 and s2 are both SymEdges in a face with > 3 sides, - * and one is not the next of the other. - * Add an edge from s1->v to s2->v, splitting the face in two. - * The original face will continue to be associated with the subface - * that has s1, and a new face will be made for s2's new face. - * Return the new diagonal's CDTEdge *. - */ -static CDTEdge *add_diagonal(CDT_state *cdt, SymEdge *s1, SymEdge *s2) -{ - CDTEdge *ediag; - CDTFace *fold, *fnew; - SymEdge *sdiag, *sdiagsym; - SymEdge *s1prev, *s1prevsym, *s2prev, *s2prevsym, *se; - BLI_assert(reachable(s1, s2, 20000)); - BLI_assert(reachable(s2, s1, 20000)); - fold = s1->face; - fnew = add_cdtface(cdt); - s1prev = prev(s1); - s1prevsym = sym(s1prev); - s2prev = prev(s2); - s2prevsym = sym(s2prev); - ediag = add_cdtedge(cdt, s1->vert, s2->vert, fnew, fold); - sdiag = &ediag->symedges[0]; - sdiagsym = &ediag->symedges[1]; - sdiag->next = s2; - sdiagsym->next = s1; - s2prev->next = sdiagsym; - s1prev->next = sdiag; - s1->rot = sdiag; - sdiag->rot = s1prevsym; - s2->rot = sdiagsym; - sdiagsym->rot = s2prevsym; -#ifdef DEBUG_CDT - BLI_assert(reachable(s2, sdiag, 2000)); -#endif - for (se = s2; se != sdiag; se = se->next) { - se->face = fnew; - } - add_list_to_input_ids(&fnew->input_ids, fold->input_ids, cdt); - return ediag; -} - -/** - * Add a dangling edge from an isolated v to the vert at se in the same face as se->face. - */ -static CDTEdge *add_vert_to_symedge_edge(CDT_state *cdt, CDTVert *v, SymEdge *se) -{ - CDTEdge *e; - SymEdge *se_rot, *se_rotsym, *new_se, *new_se_sym; - - se_rot = se->rot; - se_rotsym = sym(se_rot); - e = add_cdtedge(cdt, v, se->vert, se->face, se->face); - new_se = &e->symedges[0]; - new_se_sym = &e->symedges[1]; - new_se->next = se; - new_se_sym->next = new_se; - new_se->rot = new_se; - new_se_sym->rot = se_rot; - se->rot = new_se_sym; - se_rotsym->next = new_se_sym; - return e; -} - -/** - * Connect the verts of se1 and se2, assuming that currently those two #SymEdges are on - * the outer boundary (have face == outer_face) of two components that are isolated from - * each other. - */ -static CDTEdge *connect_separate_parts(CDT_state *cdt, SymEdge *se1, SymEdge *se2) -{ - CDTEdge *e; - SymEdge *se1_rot, *se1_rotsym, *se2_rot, *se2_rotsym, *new_se, *new_se_sym; - - BLI_assert(se1->face == cdt->outer_face && se2->face == cdt->outer_face); - se1_rot = se1->rot; - se1_rotsym = sym(se1_rot); - se2_rot = se2->rot; - se2_rotsym = sym(se2_rot); - e = add_cdtedge(cdt, se1->vert, se2->vert, cdt->outer_face, cdt->outer_face); - new_se = &e->symedges[0]; - new_se_sym = &e->symedges[1]; - new_se->next = se2; - new_se_sym->next = se1; - new_se->rot = se1_rot; - new_se_sym->rot = se2_rot; - se1->rot = new_se; - se2->rot = new_se_sym; - se1_rotsym->next = new_se; - se2_rotsym->next = new_se_sym; - return e; -} - -/** - * Split \a se at fraction \a lambda, - * and return the new #CDTEdge that is the new second half. - * Copy the edge input_ids into the new one. - */ -static CDTEdge *split_edge(CDT_state *cdt, SymEdge *se, double lambda) -{ - const double *a, *b; - double p[2]; - CDTVert *v; - CDTEdge *e; - SymEdge *sesym, *newse, *newsesym, *senext, *sesymprev, *sesymprevsym; - /* Split e at lambda. */ - a = se->vert->co; - b = se->next->vert->co; - sesym = sym(se); - sesymprev = prev(sesym); - sesymprevsym = sym(sesymprev); - senext = se->next; - p[0] = (1.0 - lambda) * a[0] + lambda * b[0]; - p[1] = (1.0 - lambda) * a[1] + lambda * b[1]; - v = add_cdtvert(cdt, p[0], p[1]); - e = add_cdtedge(cdt, v, se->next->vert, se->face, sesym->face); - sesym->vert = v; - newse = &e->symedges[0]; - newsesym = &e->symedges[1]; - se->next = newse; - newsesym->next = sesym; - newse->next = senext; - newse->rot = sesym; - sesym->rot = newse; - senext->rot = newsesym; - newsesym->rot = sesymprevsym; - sesymprev->next = newsesym; - if (newsesym->vert->symedge == sesym) { - newsesym->vert->symedge = newsesym; - } - add_list_to_input_ids(&e->input_ids, se->edge->input_ids, cdt); - return e; -} - -/** - * Delete an edge from the structure. The new combined face on either side of - * the deleted edge will be the one that was e's face. - * There will be now an unused face, marked by setting its deleted flag, - * and an unused #CDTEdge, marked by setting the next and rot pointers of - * its #SymEdge(s) to NULL. - * <pre> - * . v2 . - * / \ / \ - * /f|j\ / \ - * / | \ / \ - * | - * A | B A - * \ e| / \ / - * \ | / \ / - * \h|i/ \ / - * . v1 . - * </pre> - * Also handle variant cases where one or both ends - * are attached only to e. - */ -static void delete_edge(CDT_state *cdt, SymEdge *e) -{ - SymEdge *esym, *f, *h, *i, *j, *k, *jsym, *hsym; - CDTFace *aface, *bface; - CDTVert *v1, *v2; - bool v1_isolated, v2_isolated; - - esym = sym(e); - v1 = e->vert; - v2 = esym->vert; - aface = e->face; - bface = esym->face; - f = e->next; - h = prev(e); - i = esym->next; - j = prev(esym); - jsym = sym(j); - hsym = sym(h); - v1_isolated = (i == e); - v2_isolated = (f == esym); - - if (!v1_isolated) { - h->next = i; - i->rot = hsym; - } - if (!v2_isolated) { - j->next = f; - f->rot = jsym; - } - if (!v1_isolated && !v2_isolated && aface != bface) { - for (k = i; k != f; k = k->next) { - k->face = aface; - } - } - - /* If e was representative symedge for v1 or v2, fix that. */ - if (v1_isolated) { - v1->symedge = NULL; - } - else if (v1->symedge == e) { - v1->symedge = i; - } - if (v2_isolated) { - v2->symedge = NULL; - } - else if (v2->symedge == esym) { - v2->symedge = f; - } - - /* Mark SymEdge as deleted by setting all its pointers to NULL. */ - e->next = e->rot = NULL; - esym->next = esym->rot = NULL; - if (!v1_isolated && !v2_isolated && aface != bface) { - bface->deleted = true; - if (cdt->outer_face == bface) { - cdt->outer_face = aface; - } - } -} - -static CDT_state *cdt_init(const CDT_input *in) -{ - int i; - MemArena *arena = BLI_memarena_new(DLNY_ARENASIZE, __func__); - CDT_state *cdt = BLI_memarena_calloc(arena, sizeof(CDT_state)); - - cdt->epsilon = (double)in->epsilon; - cdt->epsilon_squared = cdt->epsilon * cdt->epsilon; - cdt->arena = arena; - cdt->input_vert_tot = in->verts_len; - cdt->vert_array_len_alloc = 2 * in->verts_len; - cdt->vert_array = BLI_memarena_alloc(arena, - cdt->vert_array_len_alloc * sizeof(*cdt->vert_array)); - cdt->listpool = BLI_mempool_create( - sizeof(LinkNode), 128 + 4 * in->verts_len, 128 + in->verts_len, 0); - - for (i = 0; i < in->verts_len; i++) { - add_cdtvert(cdt, (double)(in->vert_coords[i][0]), (double)(in->vert_coords[i][1])); - } - cdt->outer_face = add_cdtface(cdt); - return cdt; -} - -static void new_cdt_free(CDT_state *cdt) -{ - BLI_mempool_destroy(cdt->listpool); - BLI_memarena_free(cdt->arena); -} - -typedef struct SiteInfo { - CDTVert *v; - int orig_index; -} SiteInfo; - -static int site_lexicographic_cmp(const void *a, const void *b) -{ - const SiteInfo *s1 = a; - const SiteInfo *s2 = b; - const double *co1 = s1->v->co; - const double *co2 = s2->v->co; - - if (co1[0] < co2[0]) { - return -1; - } - if (co1[0] > co2[0]) { - return 1; - } - if (co1[1] < co2[1]) { - return -1; - } - if (co1[1] > co2[1]) { - return 1; - } - if (s1->orig_index < s2->orig_index) { - return -1; - } - if (s1->orig_index > s2->orig_index) { - return 1; - } - return 0; -} - -BLI_INLINE bool vert_left_of_symedge(CDTVert *v, SymEdge *se) -{ - return orient2d(v->co, se->vert->co, se->next->vert->co) > 0.0; -} - -BLI_INLINE bool vert_right_of_symedge(CDTVert *v, SymEdge *se) -{ - return orient2d(v->co, se->next->vert->co, se->vert->co) > 0.0; -} - -/* Is se above basel? */ -BLI_INLINE bool dc_tri_valid(SymEdge *se, SymEdge *basel, SymEdge *basel_sym) -{ - return orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0.0; -} - -/* Delaunay triangulate sites[start} to sites[end-1]. - * Assume sites are lexicographically sorted by coordinate. - * Return SymEdge of ccw convex hull at left-most point in *r_le - * and that of right-most point of cw convex null in *r_re. - */ -static void dc_tri( - CDT_state *cdt, SiteInfo *sites, int start, int end, SymEdge **r_le, SymEdge **r_re) -{ - int n = end - start; - int n2; - CDTVert *v1, *v2, *v3; - CDTEdge *ea, *eb, *ebasel; - SymEdge *ldo, *ldi, *rdi, *rdo, *basel, *basel_sym, *lcand, *rcand, *t; - double orient; - bool valid_lcand, valid_rcand; -#ifdef DEBUG_CDT - char label_buf[100]; - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "DC_TRI start=%d end=%d\n", start, end); - } -#endif - - BLI_assert(r_le != NULL && r_re != NULL); - if (n <= 1) { - *r_le = NULL; - *r_re = NULL; - return; - } - if (n <= 3) { - v1 = sites[start].v; - v2 = sites[start + 1].v; - ea = add_cdtedge(cdt, v1, v2, cdt->outer_face, cdt->outer_face); - ea->symedges[0].next = &ea->symedges[1]; - ea->symedges[1].next = &ea->symedges[0]; - ea->symedges[0].rot = &ea->symedges[0]; - ea->symedges[1].rot = &ea->symedges[1]; - if (n == 2) { - *r_le = &ea->symedges[0]; - *r_re = &ea->symedges[1]; - return; - } - v3 = sites[start + 2].v; - eb = add_vert_to_symedge_edge(cdt, v3, &ea->symedges[1]); - orient = orient2d(v1->co, v2->co, v3->co); - if (orient > 0.0) { - add_diagonal(cdt, &eb->symedges[0], &ea->symedges[0]); - *r_le = &ea->symedges[0]; - *r_re = &eb->symedges[0]; - } - else if (orient < 0.0) { - add_diagonal(cdt, &ea->symedges[0], &eb->symedges[0]); - *r_le = ea->symedges[0].rot; - *r_re = eb->symedges[0].rot; - } - else { - /* Collinear points. Just return a line. */ - *r_le = &ea->symedges[0]; - *r_re = &eb->symedges[0]; - } - return; - } - /* Here: n >= 4. Divide and conquer. */ - n2 = n / 2; - BLI_assert(n2 >= 2 && end - (start + n2) >= 2); - - /* Delaunay triangulate two halves, L and R. */ - dc_tri(cdt, sites, start, start + n2, &ldo, &ldi); - dc_tri(cdt, sites, start + n2, end, &rdi, &rdo); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\nDC_TRI merge step for start=%d, end=%d\n", start, end); - dump_se(ldo, "ldo"); - dump_se(ldi, "ldi"); - dump_se(rdi, "rdi"); - dump_se(rdo, "rdo"); - if (dbg_level > 1) { - sprintf(label_buf, "dc_tri(%d,%d)(%d,%d)", start, start + n2, start + n2, end); - /* dump_cdt(cdt, label_buf); */ - cdt_draw(cdt, label_buf); - } - } -#endif - - /* Find lower common tangent of L and R. */ - for (;;) { - if (vert_left_of_symedge(rdi->vert, ldi)) { - ldi = ldi->next; - } - else if (vert_right_of_symedge(ldi->vert, rdi)) { - rdi = sym(rdi)->rot; /* Previous edge to rdi with same right face. */ - } - else { - break; - } - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "common lower tangent is between\n"); - dump_se(rdi, "rdi"); - dump_se(ldi, "ldi"); - } -#endif - ebasel = connect_separate_parts(cdt, sym(rdi)->next, ldi); - basel = &ebasel->symedges[0]; - basel_sym = &ebasel->symedges[1]; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se(basel, "basel"); - cdt_draw(cdt, "after basel made"); - } -#endif - if (ldi->vert == ldo->vert) { - ldo = basel_sym; - } - if (rdi->vert == rdo->vert) { - rdo = basel; - } - - /* Merge loop. */ - for (;;) { - /* Locate the first point lcand->next->vert encountered by rising bubble, - * and delete L edges out of basel->next->vert that fail the circle test. */ - lcand = basel_sym->rot; - rcand = basel_sym->next; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "\ntop of merge loop\n"); - dump_se(lcand, "lcand"); - dump_se(rcand, "rcand"); - dump_se(basel, "basel"); - } -#endif - if (dc_tri_valid(lcand, basel, basel_sym)) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "found valid lcand\n"); - dump_se(lcand, " lcand"); - } -#endif - while (incircle(basel_sym->vert->co, - basel->vert->co, - lcand->next->vert->co, - lcand->rot->next->vert->co) > 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "incircle says to remove lcand\n"); - dump_se(lcand, " lcand"); - } -#endif - t = lcand->rot; - delete_edge(cdt, sym(lcand)); - lcand = t; - } - } - /* Symmetrically, locate first R point to be hit and delete R edges. */ - if (dc_tri_valid(rcand, basel, basel_sym)) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "found valid rcand\n"); - dump_se(rcand, " rcand"); - } -#endif - while (incircle(basel_sym->vert->co, - basel->vert->co, - rcand->next->vert->co, - sym(rcand)->next->next->vert->co) > 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "incircle says to remove rcand\n"); - dump_se(lcand, " rcand"); - } -#endif - t = sym(rcand)->next; - delete_edge(cdt, rcand); - rcand = t; - } - } - /* If both lcand and rcand are invalid, then basel is the common upper tangent. */ - valid_lcand = dc_tri_valid(lcand, basel, basel_sym); - valid_rcand = dc_tri_valid(rcand, basel, basel_sym); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf( - stderr, "after bubbling up, valid_lcand=%d, valid_rcand=%d\n", valid_lcand, valid_rcand); - dump_se(lcand, "lcand"); - dump_se(rcand, "rcand"); - } -#endif - if (!valid_lcand && !valid_rcand) { - break; - } - /* The next cross edge to be connected is to either lcand->next->vert or rcand->next->vert; - * if both are valid, choose the appropriate one using the incircle test. - */ - if (!valid_lcand || - (valid_rcand && - incircle(lcand->next->vert->co, lcand->vert->co, rcand->vert->co, rcand->next->vert->co) > - 0.0)) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "connecting rcand\n"); - dump_se(basel_sym, " se1=basel_sym"); - dump_se(rcand->next, " se2=rcand->next"); - } -#endif - ebasel = add_diagonal(cdt, rcand->next, basel_sym); - } - else { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "connecting lcand\n"); - dump_se(sym(lcand), " se1=sym(lcand)"); - dump_se(basel_sym->next, " se2=basel_sym->next"); - } -#endif - ebasel = add_diagonal(cdt, basel_sym->next, sym(lcand)); - } - basel = &ebasel->symedges[0]; - basel_sym = &ebasel->symedges[1]; - BLI_assert(basel_sym->face == cdt->outer_face); -#ifdef DEBUG_CDT - if (dbg_level > 2) { - cdt_draw(cdt, "after adding new crossedge"); - // dump_cdt(cdt, "after adding new crossedge"); - } -#endif - } - *r_le = ldo; - *r_re = rdo; - BLI_assert(sym(ldo)->face == cdt->outer_face && rdo->face == cdt->outer_face); -} - -/* Guibas-Stolfi Divide-and_Conquer algorithm. */ -static void dc_triangulate(CDT_state *cdt, SiteInfo *sites, int nsites) -{ - int i, j, n; - SymEdge *le, *re; - - /* Compress sites in place to eliminated verts that merge to others. */ - i = 0; - j = 0; - while (j < nsites) { - /* Invariante: sites[0..i-1] have non-merged verts from 0..(j-1) in them. */ - sites[i] = sites[j++]; - if (sites[i].v->merge_to_index < 0) { - i++; - } - } - n = i; - if (n == 0) { - return; - } - dc_tri(cdt, sites, 0, n, &le, &re); -} - -/** - * Do a Delaunay Triangulation of the points in cdt->vert_array. - * This is only a first step in the Constrained Delaunay triangulation, - * because it doesn't yet deal with the segment constraints. - * The algorithm used is the Divide & Conquer algorithm from the - * Guibas-Stolfi "Primitives for the Manipulation of General Subdivision - * and the Computation of Voronoi Diagrams" paper. - * The data structure here is similar to but not exactly the same as - * the quad-edge structure described in that paper. - * The incircle and ccw tests are done using Shewchuk's exact - * primitives (see below), so that this routine is robust. - * - * As a preprocessing step, we want to merge all vertices that are - * within cdt->epsilon of each other. This is accomplished by lexicographically - * sorting the coordinates first (which is needed anyway for the D&C algorithm). - * The CDTVerts with merge_to_index not equal to -1 are after this regarded - * as having been merged into the vertex with the corresponding index. - */ -static void initial_triangulation(CDT_state *cdt) -{ - int i, j, n; - SiteInfo *sites; - double *ico, *jco; - double xend, yend, xcur; - double epsilon = cdt->epsilon; - double epsilon_squared = cdt->epsilon_squared; -#ifdef SJF_WAY - CDTEdge *e; - CDTVert *va, *vb; -#endif -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nINITIAL TRIANGULATION\n\n"); - } -#endif - - /* First sort the vertices by lexicographic order of their - * coordinates, breaking ties by putting earlier original-index - * vertices first. - */ - n = cdt->vert_array_len; - if (n <= 1) { - return; - } - sites = MEM_malloc_arrayN(n, sizeof(SiteInfo), __func__); - for (i = 0; i < n; i++) { - sites[i].v = cdt->vert_array[i]; - sites[i].orig_index = i; - } - qsort(sites, n, sizeof(SiteInfo), site_lexicographic_cmp); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "after sorting\n"); - for (i = 0; i < n; i++) { - fprintf(stderr, "%d: orig index: %d, (%f,%f)\n", i, sites[i].orig_index, F2(sites[i].v->co)); - } - } -#endif - - /* Now de-duplicate according to user-defined epsilon. - * We will merge a vertex into an earlier-indexed vertex - * that is within epsilon (Euclidean distance). - * Merges may cascade. So we may end up merging two things - * that are farther than epsilon by transitive merging. Oh well. - * Assume that merges are rare, so use simple searches in the - * lexicographic ordering - likely we will soon hit y's with - * the same x that are farther away than epsilon, and then - * skipping ahead to the next biggest x, are likely to soon - * find one of those farther away than epsilon. - */ - for (i = 0; i < n - 1; i++) { - ico = sites[i].v->co; - /* Start j at next place that has both x and y coords within epsilon. */ - xend = ico[0] + epsilon; - yend = ico[1] + epsilon; - j = i + 1; - while (j < n) { - jco = sites[j].v->co; - if (jco[0] > xend) { - break; /* No more j's to process. */ - } - if (jco[1] > yend) { - /* Get past any string of v's with the same x and too-big y. */ - xcur = jco[0]; - while (++j < n) { - if (sites[j].v->co[0] > xcur) { - break; - } - } - BLI_assert(j == n || sites[j].v->co[0] > xcur); - if (j == n) { - break; - } - jco = sites[j].v->co; - if (jco[0] > xend || jco[1] > yend) { - break; - } - } - /* When here, vertex i and j are within epsilon by box test. - * The Euclidean distance test is stricter, so need to do it too, now. - */ - BLI_assert(j < n && jco[0] <= xend && jco[1] <= yend); - if (len_squared_v2v2_db(ico, jco) <= epsilon_squared) { - sites[j].v->merge_to_index = (sites[i].v->merge_to_index == -1) ? - sites[i].orig_index : - sites[i].v->merge_to_index; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, - "merged orig vert %d to %d\n", - sites[j].orig_index, - sites[j].v->merge_to_index); - } -#endif - } - j++; - } - } - - /* Now add non-dup vertices into triangulation in lexicographic order. */ - - dc_triangulate(cdt, sites, n); - MEM_freeN(sites); -} - -/** - * Use #LinkNode linked list as stack of #SymEdges, allocating from `cdt->listpool` . - */ -typedef LinkNode *Stack; - -BLI_INLINE void push(Stack *stack, SymEdge *se, CDT_state *cdt) -{ - BLI_linklist_prepend_pool(stack, se, cdt->listpool); -} - -BLI_INLINE SymEdge *pop(Stack *stack, CDT_state *cdt) -{ - return (SymEdge *)BLI_linklist_pop_pool(stack, cdt->listpool); -} - -BLI_INLINE bool is_empty(Stack *stack) -{ - return *stack == NULL; -} - -/** - * Re-triangulates, assuring constrained delaunay condition, - * the pseudo-polygon that cycles from se. - * "pseudo" because a vertex may be repeated. - * See Anglada paper, "An Improved incremental algorithm - * for constructing restricted Delaunay triangulations". - */ -static void re_delaunay_triangulate(CDT_state *cdt, SymEdge *se) -{ - SymEdge *ss, *first, *cse; - CDTVert *a, *b, *c, *v; - CDTEdge *ebc, *eca; - int count; -#ifdef DEBUG_CDT - SymEdge *last; - const int dbg_level = 0; -#endif - - if (se->face == cdt->outer_face || sym(se)->face == cdt->outer_face) { - return; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "retriangulate"); - dump_se_cycle(se, "poly ", 1000); - } -#endif - /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */ - count = 1; - for (ss = se->next; ss != se; ss = ss->next) { - count++; - } - if (count <= 3) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "nothing to do\n"); - } -#endif - return; - } - /* First and last are the SymEdges whose verts are first and last off of base, - * continuing from 'se'. */ - first = se->next->next; - /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */ - a = se->vert; - b = se->next->vert; - c = first->vert; - cse = first; -#ifdef DEBUG_CDT - last = prev(se); - if (dbg_level > 1) { - dump_se(first, "first"); - dump_se(last, "last"); - dump_v(a, "a"); - dump_v(b, "b"); - dump_v(c, "c"); - } -#endif - for (ss = first->next; ss != se; ss = ss->next) { - v = ss->vert; - if (incircle(a->co, b->co, c->co, v->co) > 0.0) { - c = v; - cse = ss; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_v(c, "new c "); - } -#endif - } - } - /* Add diagonals necessary to make abc a triangle. */ -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "make triangle abc exist where\n"); - dump_v(a, " a"); - dump_v(b, " b"); - dump_v(c, " c"); - } -#endif - ebc = NULL; - eca = NULL; - if (!exists_edge(b, c)) { - ebc = add_diagonal(cdt, se->next, cse); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "added edge ebc\n"); - dump_se(&ebc->symedges[0], " ebc"); - } -#endif - } - if (!exists_edge(c, a)) { - eca = add_diagonal(cdt, cse, se); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "added edge eca\n"); - dump_se(&eca->symedges[0], " eca"); - } -#endif - } - /* Now recurse. */ - if (ebc) { - re_delaunay_triangulate(cdt, &ebc->symedges[1]); - } - if (eca) { - re_delaunay_triangulate(cdt, &eca->symedges[1]); - } -} - -static double tri_orient(const SymEdge *t) -{ - return orient2d(t->vert->co, t->next->vert->co, t->next->next->vert->co); -} - -/** - * The CrossData struct gives defines either an endpoint or an intermediate point - * in the path we will take to insert an edge constraint. - * Each such point will either be - * (a) a vertex or - * (b) a fraction lambda (0 < lambda < 1) along some #SymEdge.] - * - * In general, lambda=0 indicates case a and lambda != 0 indicates case be. - * The 'in' edge gives the destination attachment point of a diagonal from the previous crossing, - * and the 'out' edge gives the origin attachment point of a diagonal to the next crossing. - * But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL. - * - * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the #SymEdge - * from 'vert' that has as face the one that you go through to get to this vertex. If you go - * exactly along an edge then we set 'in' to NULL, since it won't be needed. The first crossing - * will have 'in' = NULL. We set 'out' to the #SymEdge that has the face we go though to get to the - * next crossing, or, if the next crossing is a case (a), then it is the edge that goes to that - * next vertex. 'out' wlll be NULL for the last one. - * - * For case (b), vert will be NULL at first, and later filled in with the created split vertex, - * and 'in' will be the #SymEdge that we go through, and lambda will be between 0 and 1, - * the fraction from in's vert to in->next's vert to put the split vertex. - * 'out' is not needed in this case, since the attachment point will be the sym of the first - * half of the split edge. - */ -typedef struct CrossData { - double lambda; - CDTVert *vert; - SymEdge *in; - SymEdge *out; -} CrossData; - -static bool get_next_crossing_from_vert(CDT_state *cdt, - CrossData *cd, - CrossData *cd_next, - const CDTVert *v2); - -/** - * As part of finding crossings, we found a case where the next crossing goes through vert v. - * If it came from a previous vert in cd, then cd_out is the edge that leads from that to v. - * Else cd_out can be NULL, because it won't be used. - * Set *cd_next to indicate this. We can set 'in' but not 'out'. We can set the 'out' of the - * current cd. - */ -static void fill_crossdata_for_through_vert(CDTVert *v, - SymEdge *cd_out, - CrossData *cd, - CrossData *cd_next) -{ - SymEdge *se; -#ifdef DEBUG_CDT - int dbg_level = 0; -#endif - - cd_next->lambda = 0.0; - cd_next->vert = v; - cd_next->in = NULL; - cd_next->out = NULL; - if (cd->lambda == 0.0) { - cd->out = cd_out; - } - else { - /* One of the edges in the triangle with edge sym(cd->in) contains v. */ - se = sym(cd->in); - if (se->vert != v) { - se = se->next; - if (se->vert != v) { - se = se->next; - } - } - BLI_assert(se->vert == v); - cd_next->in = se; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_cross_data(cd, "cd through vert, cd"); - dump_cross_data(cd_next, "cd_next through vert, cd"); - } -#endif -} - -/** - * As part of finding crossings, we found a case where orient tests say that the next crossing - * is on the #SymEdge t, while intersecting with the ray from \a curco to \a v2. - * Find the intersection point and fill in the #CrossData for that point. - * It may turn out that when doing the intersection, we get an answer that says that - * this case is better handled as through-vertex case instead, so we may do that. - * In the latter case, we want to avoid a situation where the current crossing is on an edge - * and the next will be an endpoint of the same edge. When that happens, we "rewrite history" - * and turn the current crossing into a vert one, and then extend from there. - * - * We cannot fill cd_next's 'out' edge yet, in the case that the next one ends up being a vert - * case. We need to fill in cd's 'out' edge if it was a vert case. - */ -static void fill_crossdata_for_intersect(CDT_state *cdt, - const double *curco, - const CDTVert *v2, - SymEdge *t, - CrossData *cd, - CrossData *cd_next) -{ - CDTVert *va, *vb, *vc; - double lambda, mu, len_ab; - SymEdge *se_vcva, *se_vcvb; - int isect; -#ifdef DEBUG_CDT - int dbg_level = 0; -#endif - - va = t->vert; - vb = t->next->vert; - vc = t->next->next->vert; - se_vcvb = sym(t->next); - se_vcva = t->next->next; - BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va); - BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb); - UNUSED_VARS_NDEBUG(vc); - isect = isect_seg_seg_v2_lambda_mu_db(va->co, vb->co, curco, v2->co, &lambda, &mu); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - double co[2]; - fprintf(stderr, "crossdata for intersect gets lambda=%.17g, mu=%.17g\n", lambda, mu); - fprintf(stderr, - "isect=%s\n", - isect == 2 ? "cross" : (isect == 1 ? "exact" : (isect == 0 ? "none" : "colinear"))); - fprintf(stderr, - "va=v%d=(%g,%g), vb=v%d=(%g,%g), vc=v%d, curco=(%g,%g), v2=(%g,%g)\n", - va->index, - F2(va->co), - vb->index, - F2(vb->co), - vc->index, - F2(curco), - F2(v2->co)); - dump_se_short(se_vcva, "vcva="); - dump_se_short(se_vcvb, " vcvb="); - interp_v2_v2v2_db(co, va->co, vb->co, lambda); - fprintf(stderr, "\nco=(%.17g,%.17g)\n", F2(co)); - } -#endif - switch (isect) { - case ISECT_LINE_LINE_CROSS: - len_ab = len_v2v2_db(va->co, vb->co); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, - "len_ab=%g, near a=%g, near b=%g\n", - len_ab, - lambda * len_ab, - (1.0 - lambda) * len_ab); - } -#endif - if (lambda * len_ab <= cdt->epsilon) { - fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); - } - else if ((1.0 - lambda) * len_ab <= cdt->epsilon) { - fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); - } - else { - *cd_next = (CrossData){lambda, NULL, t, NULL}; - if (cd->lambda == 0.0) { - cd->out = se_vcva; - } - } - break; - case ISECT_LINE_LINE_EXACT: - if (lambda == 0.0) { - fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); - } - else if (lambda == 1.0) { - fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); - } - else { - *cd_next = (CrossData){lambda, NULL, t, NULL}; - if (cd->lambda == 0.0) { - cd->out = se_vcva; - } - } - break; - case ISECT_LINE_LINE_NONE: - /* It should be very near one end or other of segment. */ - if (lambda <= 0.5) { - fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); - } - else { - fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); - } - break; - case ISECT_LINE_LINE_COLINEAR: - if (len_squared_v2v2_db(va->co, v2->co) <= len_squared_v2v2_db(vb->co, v2->co)) { - fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); - } - else { - fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); - } - break; - } -} - -/** - * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming - * 'cd' represents a crossing that goes through a vertex. - * - * We do a rotational scan around cd's vertex, looking for the triangle where the ray from cd->vert - * to v2 goes between the two arms from cd->vert, or where it goes along one of the edges. - */ -static bool get_next_crossing_from_vert(CDT_state *cdt, - CrossData *cd, - CrossData *cd_next, - const CDTVert *v2) -{ - SymEdge *t, *tstart; - CDTVert *vcur, *va, *vb; - double orient1, orient2; - bool ok; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nget_next_crossing_from_vert\n"); - dump_v(cd->vert, " cd->vert"); - } -#endif - - t = tstart = cd->vert->symedge; - vcur = cd->vert; - ok = false; - do { - /* - * The ray from vcur to v2 has to go either between two successive - * edges around vcur or exactly along them. This time through the - * loop, check to see if the ray goes along vcur-va - * or between vcur-va and vcur-vb, where va is the end of t - * and vb is the next vertex (on the next rot edge around vcur, but - * should also be the next vert of triangle starting with vcur-va. - */ - if (t->face != cdt->outer_face && tri_orient(t) < 0.0) { - fprintf(stderr, "BAD TRI orientation %g\n", tri_orient(t)); /* TODO: handle this */ -#ifdef DEBUG_CDT - dump_se_cycle(t, "top of ccw scan loop", 4); -#endif - } - va = t->next->vert; - vb = t->next->next->vert; - orient1 = orient2d(t->vert->co, va->co, v2->co); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "non-final through vert case\n"); - dump_v(va, " va"); - dump_v(vb, " vb"); - fprintf(stderr, "orient1=%g\n", orient1); - } -#endif - if (orient1 == 0.0 && in_line(vcur->co, va->co, v2->co)) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "ray goes through va\n"); - } -#endif - fill_crossdata_for_through_vert(va, t, cd, cd_next); - ok = true; - break; - } - if (t->face != cdt->outer_face) { - orient2 = orient2d(vcur->co, vb->co, v2->co); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "orient2=%g\n", orient2); - } -#endif - /* Don't handle orient2 == 0.0 case here: next rotation will get it. */ - if (orient1 > 0.0 && orient2 < 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "segment intersects\n"); - } -#endif - t = t->next; - fill_crossdata_for_intersect(cdt, vcur->co, v2, t, cd, cd_next); - ok = true; - break; - } - } - } while ((t = t->rot) != tstart); -#ifdef DEBUG_CDT - if (!ok) { - /* Didn't find the exit! Shouldn't happen. */ - fprintf(stderr, "shouldn't happen: can't find next crossing from vert\n"); - } -#endif - return ok; -} - -/** - * As part of finding the crossings of a ray to 'v2', find the next crossing after 'cd', assuming - * 'cd' represents a crossing that goes through a an edge, not at either end of that edge. - * - * We have the triangle 'vb-va-vc', where va and vb are the split edge and 'vc' is the third vertex - * on that new side of the edge (should be closer to v2). The next crossing should be through 'vc' - * or intersecting 'vb-vc' or 'va-vc'. - */ -static void get_next_crossing_from_edge(CDT_state *cdt, - CrossData *cd, - CrossData *cd_next, - const CDTVert *v2) -{ - double curco[2]; - double orient; - CDTVert *va, *vb, *vc; - SymEdge *se_ac; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nget_next_crossing_from_edge\n"); - fprintf(stderr, " lambda=%.17g\n", cd->lambda); - dump_se_short(cd->in, " cd->in"); - } -#endif - - va = cd->in->vert; - vb = cd->in->next->vert; - interp_v2_v2v2_db(curco, va->co, vb->co, cd->lambda); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, " curco=(%.17g,%.17g)\n", F2(curco)); - } -#endif - se_ac = sym(cd->in)->next; - vc = se_ac->next->vert; - orient = orient2d(curco, v2->co, vc->co); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "now searching with third vertex "); - dump_v(vc, "vc"); - fprintf(stderr, "orient2d(cur, v2, vc) = %g\n", orient); - } -#endif - if (orient < 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "curco--v2 intersects vb--vc\n"); - } -#endif - fill_crossdata_for_intersect(cdt, curco, v2, se_ac->next, cd, cd_next); - } - else if (orient > 0.0) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "curco--v2 intersects va--vc\n"); - } -#endif - fill_crossdata_for_intersect(cdt, curco, v2, se_ac, cd, cd_next); - } - else { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "orient==0 case, so going through or to vc\n"); - } -#endif - *cd_next = (CrossData){0.0, vc, se_ac->next, NULL}; - } -} - -/** - * Add a constrained edge between v1 and v2 to cdt structure. - * This may result in a number of #CDTEdges created, due to intersections - * and partial overlaps with existing cdt vertices and edges. - * Each created #CDTEdge will have input_id added to its input_ids list. - * - * If \a r_edges is not NULL, the #CDTEdges generated or found that go from - * v1 to v2 are put into that linked list, in order. - * - * Assumes that #BLI_constrained_delaunay_get_output has not been called yet. - */ -static void add_edge_constraint( - CDT_state *cdt, CDTVert *v1, CDTVert *v2, int input_id, LinkNode **r_edges) -{ - SymEdge *t, *se, *tstart, *tnext; - int i, j, n, visit; - bool ok; - CrossData *crossings = NULL; - CrossData *cd, *cd_prev, *cd_next; - CDTVert *v; - CDTEdge *edge; - LinkNodePair edge_list = {NULL, NULL}; - BLI_array_staticdeclare(crossings, 128); -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nADD_EDGE_CONSTRAINT\n"); - dump_v(v1, " 1"); - dump_v(v2, " 2"); - } -#endif - - if (r_edges) { - *r_edges = NULL; - } - - /* - * Handle two special cases first: - * 1) The two end vertices are the same (can happen because of merging). - * 2) There is already an edge between v1 and v2. - */ - if (v1 == v2) { - return; - } - if ((t = find_symedge_between_verts(v1, v2)) != NULL) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "segment already there\n"); - } -#endif - add_to_input_ids(&t->edge->input_ids, input_id, cdt); - if (r_edges != NULL) { - BLI_linklist_append_pool(&edge_list, t->edge, cdt->listpool); - *r_edges = edge_list.list; - } - return; - } - - /* - * Fill crossings array with CrossData points for intersection path from v1 to v2. - * - * At every point, the crossings array has the path so far, except that - * the .out field of the last element of it may not be known yet -- if that - * last element is a vertex, then we won't know the output edge until we - * find the next crossing. - * - * To protect against infinite loops, we keep track of which vertices - * we have visited by setting their visit_index to a new visit epoch. - * - * We check a special case first: where the segment is already there in - * one hop. Saves a bunch of orient2d tests in that common case. - */ - visit = ++cdt->visit_count; - BLI_array_grow_one(crossings); - crossings[0] = (CrossData){0.0, v1, NULL, NULL}; - while (!((n = BLI_array_len(crossings)) > 0 && crossings[n - 1].vert == v2)) { - BLI_array_grow_one(crossings); - cd = &crossings[n - 1]; - cd_next = &crossings[n]; - if (crossings[n - 1].lambda == 0.0) { - ok = get_next_crossing_from_vert(cdt, cd, cd_next, v2); - } - else { - get_next_crossing_from_edge(cdt, cd, cd_next, v2); - } - if (!ok || BLI_array_len(crossings) == 100000) { - /* Shouldn't happen but if does, just bail out. */ -#ifdef DEBUG_CDT - fprintf(stderr, "FAILURE adding segment, bailing out\n"); -#endif - BLI_array_free(crossings); - return; - } -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "crossings[%d]: ", n); - dump_cross_data(&crossings[n], ""); - } -#endif - if (crossings[n].lambda == 0.0) { - if (crossings[n].vert->visit_index == visit) { - fprintf(stderr, "WHOOPS, REVISIT. Bailing out.\n"); /*TODO: desperation search here. */ - BLI_array_free(crossings); - return; - } - crossings[n].vert->visit_index = visit; - } - } - -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\ncrossings found\n"); - for (i = 0; i < BLI_array_len(crossings); i++) { - fprintf(stderr, "%d: ", i); - dump_cross_data(&crossings[i], ""); - if (crossings[i].lambda == 0.0) { - if (i == 0 || crossings[i - 1].lambda == 0.0) { - BLI_assert(crossings[i].in == NULL); - } - else { - BLI_assert(crossings[i].in != NULL && crossings[i].in->vert == crossings[i].vert); - BLI_assert(crossings[i].in->face == sym(crossings[i - 1].in)->face); - } - if (i == BLI_array_len(crossings) - 1) { - BLI_assert(crossings[i].vert == v2); - BLI_assert(crossings[i].out == NULL); - } - else { - BLI_assert(crossings[i].out->vert == crossings[i].vert); - if (crossings[i + 1].lambda == 0.0) { - BLI_assert(crossings[i].out->next->vert == crossings[i + 1].vert); - } - else { - BLI_assert(crossings[i].out->face == crossings[i + 1].in->face); - } - } - } - else { - if (i > 0 && crossings[i - 1].lambda == 0.0) { - BLI_assert(crossings[i].in->face == crossings[i - 1].out->face); - } - BLI_assert(crossings[i].out == NULL); - } - } - } -#endif - - /* - * Postprocess crossings. - * Some crossings may have an intersection crossing followed - * by a vertex crossing that is on the same edge that was just - * intersected. We prefer to go directly from the previous - * crossing directly to the vertex. This may chain backwards. - * - * This loop marks certain crossings as "deleted", by setting - * their lambdas to -1.0. - */ - for (i = 2; i < BLI_array_len(crossings); i++) { - cd = &crossings[i]; - if (cd->lambda == 0.0) { - v = cd->vert; - for (j = i - 1; j > 0; j--) { - cd_prev = &crossings[j]; - if ((cd_prev->lambda == 0.0 && cd_prev->vert != v) || - (cd_prev->lambda != 0.0 && cd_prev->in->vert != v && cd_prev->in->next->vert != v)) { - break; - } - cd_prev->lambda = -1.0; /* Mark cd_prev as 'deleted'. */ -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "deleted crossing %d\n", j); - } -#endif - } - if (j < i - 1) { - /* Some crossings were deleted. Fix the in and out edges across gap. */ - cd_prev = &crossings[j]; - if (cd_prev->lambda == 0.0) { - se = find_symedge_between_verts(cd_prev->vert, v); - if (se == NULL) { -#ifdef DEBUG_CDT - fprintf(stderr, "FAILURE(a) in delete crossings, bailing out.\n"); -#endif - BLI_array_free(crossings); - return; - } - cd_prev->out = se; - cd->in = NULL; - } - else { - se = find_symedge_with_face(v, sym(cd_prev->in)->face); - if (se == NULL) { -#ifdef DEBUG_CDT - fprintf(stderr, "FAILURE(b) in delete crossings, bailing out.\n"); -#endif - BLI_array_free(crossings); - return; - } - cd->in = se; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "after deleting crossings:\n"); - fprintf(stderr, "cross[%d]: ", j); - dump_cross_data(cd_prev, ""); - fprintf(stderr, "cross[%d]: ", i); - dump_cross_data(cd, ""); - } -#endif - } - } - } - - /* - * Insert all intersection points on constrained edges. - */ - for (i = 0; i < BLI_array_len(crossings); i++) { - cd = &crossings[i]; - if (cd->lambda != 0.0 && cd->lambda != -1.0 && is_constrained_edge(cd->in->edge)) { - edge = split_edge(cdt, cd->in, cd->lambda); - cd->vert = edge->symedges[0].vert; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "insert vert for crossing %d: ", i); - dump_v(cd->vert, "inserted"); - } -#endif - } - } - - /* - * Remove any crossed, non-intersected edges. - */ - for (i = 0; i < BLI_array_len(crossings); i++) { - cd = &crossings[i]; - if (cd->lambda != 0.0 && cd->lambda != -1.0 && !is_constrained_edge(cd->in->edge)) { - delete_edge(cdt, cd->in); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "delete edge for crossing %d\n", i); - } -#endif - } - } - - /* - * Insert segments for v1->v2. - */ - tstart = crossings[0].out; - for (i = 1; i < BLI_array_len(crossings); i++) { - cd = &crossings[i]; - if (cd->lambda == -1.0) { - continue; /* This crossing was deleted. */ - } - t = tnext = NULL; - if (cd->lambda != 0.0) { - if (is_constrained_edge(cd->in->edge)) { - t = cd->vert->symedge; - tnext = sym(t)->next; - } - } - else if (cd->lambda == 0.0) { - t = cd->in; - tnext = cd->out; - if (t == NULL) { - /* Previous non-deleted crossing must also have been a vert, and segment should exist. */ - for (j = i - 1; j >= 0; j--) { - cd_prev = &crossings[j]; - if (cd_prev->lambda != -1.0) { - break; - } - } - BLI_assert(cd_prev->lambda == 0.0); - BLI_assert(cd_prev->out->next->vert == cd->vert); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "edge to crossing %d already there\n", i); - } -#endif - edge = cd_prev->out->edge; - add_to_input_ids(&edge->input_ids, input_id, cdt); - } - } - if (t != NULL) { -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "edge to crossing %d: insert diagonal between\n", i); - dump_se(tstart, " "); - dump_se(t, " "); - dump_se_cycle(tstart, "tstart", 100); - dump_se_cycle(t, "t", 100); - } -#endif - if (tstart->next->vert == t->vert) { - edge = tstart->edge; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "already there (b)\n"); - } -#endif - } - else { - edge = add_diagonal(cdt, tstart, t); - } - add_to_input_ids(&edge->input_ids, input_id, cdt); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "added\n"); - } -#endif - if (r_edges != NULL) { - BLI_linklist_append_pool(&edge_list, edge, cdt->listpool); - } - /* Now retriangulate upper and lower gaps. */ - re_delaunay_triangulate(cdt, &edge->symedges[0]); - re_delaunay_triangulate(cdt, &edge->symedges[1]); - } - if (i < BLI_array_len(crossings) - 1) { - if (tnext != NULL) { - tstart = tnext; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - fprintf(stderr, "now tstart = "); - dump_se(tstart, ""); - } -#endif - } - } - } - - if (r_edges) { - *r_edges = edge_list.list; - } - BLI_array_free(crossings); -} - -/** - * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that - * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is - * on the left of that SymEdge. - * - * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then - * process all adjacent faces where the adjacency isn't across an edge that was a constraint added - * for the boundary of the input face. - * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face. - * - * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside. - * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the - * contiguous set of faces enclosed by parts of the boundary, leaving the other such untagged. This - * may be a feature instead of a bug if the first contiguous section is most of the face and the - * others are tiny self-crossing triangles at some parts of the boundary. On the other hand, if - * decide we want to handle these in full generality, then will need a more complicated algorithm - * (using "inside" tests and a parity rule) to decide on the interior. - */ -static void add_face_ids( - CDT_state *cdt, SymEdge *face_symedge, int face_id, int fedge_start, int fedge_end) -{ - Stack stack; - SymEdge *se, *se_start, *se_sym; - CDTFace *face, *face_other; - int visit; - - /* Can't loop forever since eventually would visit every face. */ - cdt->visit_count++; - visit = cdt->visit_count; - stack = NULL; - push(&stack, face_symedge, cdt); - while (!is_empty(&stack)) { - se = pop(&stack, cdt); - face = se->face; - if (face->visit_index == visit) { - continue; - } - face->visit_index = visit; - add_to_input_ids(&face->input_ids, face_id, cdt); - se_start = se; - for (se = se->next; se != se_start; se = se->next) { - if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) { - se_sym = sym(se); - face_other = se_sym->face; - if (face_other->visit_index != visit) { - push(&stack, se_sym, cdt); - } - } - } - } -} - -/* Delete_edge but try not to mess up outer face. - * Also faces have symedges now, so make sure not - * to mess those up either. */ -static void dissolve_symedge(CDT_state *cdt, SymEdge *se) -{ - SymEdge *symse = sym(se); - if (symse->face == cdt->outer_face) { - se = sym(se); - symse = sym(se); - } - if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) { - /* Advancing by 2 to get past possible 'sym(se)'. */ - if (se->next->next == se) { - cdt->outer_face->symedge = NULL; - } - else { - cdt->outer_face->symedge = se->next->next; - } - } - else { - if (se->face->symedge == se) { - se->face->symedge = se->next; - } - if (symse->face->symedge == symse) { - symse->face->symedge = symse->next; - } - } - delete_edge(cdt, se); -} - -/* Slow way to get face and start vertex index within face for edge id e. */ -static bool get_face_edge_id_indices(const CDT_input *in, int e, int *r_f, int *r_fi) -{ - int f; - int id; - - id = in->edges_len; - if (e < id) { - return false; - } - for (f = 0; f < in->faces_len; f++) { - if (e < id + in->faces_len_table[f]) { - *r_f = f; - *r_fi = e - id; - return true; - } - id += in->faces_len_table[f]; - } - return false; -} - -/* Is pt_co when snapped to segment seg1 seg2 all of: - * a) strictly within that segment - * b) within epsilon from original pt_co - * c) pt_co is not within epsilon of either seg1 or seg2. - * Return true if so, and return in *r_lambda the fraction of the way from seg1 to seg2 of the - * snapped point. - */ -static bool check_vert_near_segment(const double *pt_co, - const double *seg1, - const double *seg2, - double epsilon_squared, - double *r_lambda) -{ - double lambda, snap_co[2]; - - lambda = closest_to_line_v2_db(snap_co, pt_co, seg1, seg2); - *r_lambda = lambda; - if (lambda <= 0.0 || lambda >= 1.0) { - return false; - } - if (len_squared_v2v2_db(pt_co, snap_co) > epsilon_squared) { - return false; - } - if (len_squared_v2v2_db(pt_co, seg1) <= epsilon_squared || - len_squared_v2v2_db(pt_co, seg2) <= epsilon_squared) { - return false; - } - return true; -} - -typedef struct EdgeVertLambda { - int e_id; - int v_id; - double lambda; -} EdgeVertLambda; - -/* For sorting first by edge id, then by lambda, then by vert id. */ -static int evl_cmp(const void *a, const void *b) -{ - const EdgeVertLambda *area = a; - const EdgeVertLambda *sb = b; - - if (area->e_id < sb->e_id) { - return -1; - } - if (area->e_id > sb->e_id) { - return 1; - } - if (area->lambda < sb->lambda) { - return -1; - } - if (area->lambda > sb->lambda) { - return 1; - } - if (area->v_id < sb->v_id) { - return -1; - } - if (area->v_id > sb->v_id) { - return 1; - } - return 0; -} - -/** - * If epsilon > 0, and input doesn't have skip_modify_input == true, - * check input to see if any constraint edge ends (including face edges) come - * within epsilon of another edge. - * For all such cases, we want to split the constraint edge at the point nearest to near vertex - * and move the vertex coordinates to be on that edge. - * But exclude cases where they come within epsilon of either end because those will be handled - * by vertex merging in the main triangulation algorithm. - * - * If any such splits are found, make a new CDT_input reflecting this change, and provide an - * edge map to map from edge ids in the new input space to edge ids in the old input space. - * - * TODO: replace naive O(n^2) algorithm with kdopbvh-based one. - */ -static const CDT_input *modify_input_for_near_edge_ends(const CDT_input *input, int **r_edge_map) -{ - CDT_input *new_input = NULL; - int e, eprev, e1, e2, f, fi, flen, start, i, j; - int i_new, i_old, i_evl; - int v11, v12, v21, v22; - double co11[2], co12[2], co21[2], co22[2]; - double lambda; - double eps = (double)input->epsilon; - double eps_sq = eps * eps; - int tot_edge_constraints, edges_len, tot_face_edges; - int new_tot_face_edges, new_tot_con_edges; - int delta_con_edges, delta_face_edges, cur_e_cnt; - int *edge_map; - int evl_len; - EdgeVertLambda *edge_vert_lambda = NULL; - BLI_array_staticdeclare(edge_vert_lambda, 128); -#ifdef DEBUG_CDT - EdgeVertLambda *evl; - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nMODIFY INPUT\n\n"); - } -#endif - - *r_edge_map = NULL; - if (input->epsilon == 0.0 || input->skip_input_modify || - (input->edges_len == 0 && input->faces_len == 0)) { - return input; - } - - /* Edge constraints are union of the explicitly provided edges and the implicit edges - * that are part of the provided faces. We index constraints by have the first input->edges_len - * ints standing for the explicit edge with the same index, and the rest being face edges in - * the order that the faces appear and then edges within those faces, with indices offset by - * input->edges_len. - * Calculate tot_edge_constraints to be the sum of the two kinds of edges. - * We first have to count the number of face edges. - * That is the same as the number of vertices in the faces table, which - * we can find by adding the last length to the last start. - */ - edges_len = input->edges_len; - tot_edge_constraints = edges_len; - if (input->faces_len > 0) { - tot_face_edges = input->faces_start_table[input->faces_len - 1] + - input->faces_len_table[input->faces_len - 1]; - } - else { - tot_face_edges = 0; - } - tot_edge_constraints = edges_len + tot_face_edges; - - for (e1 = 0; e1 < tot_edge_constraints - 1; e1++) { - if (e1 < edges_len) { - v11 = input->edges[e1][0]; - v12 = input->edges[e1][1]; - } - else { - if (!get_face_edge_id_indices(input, e1, &f, &fi)) { - /* Must be bad input. Will be caught later so don't need to signal here. */ - continue; - } - start = input->faces_start_table[f]; - flen = input->faces_len_table[f]; - v11 = input->faces[start + fi]; - v12 = input->faces[(fi == flen - 1) ? start : start + fi + 1]; - } - for (e2 = e1 + 1; e2 < tot_edge_constraints; e2++) { - if (e2 < edges_len) { - v21 = input->edges[e2][0]; - v22 = input->edges[e2][1]; - } - else { - if (!get_face_edge_id_indices(input, e2, &f, &fi)) { - continue; - } - start = input->faces_start_table[f]; - flen = input->faces_len_table[f]; - v21 = input->faces[start + fi]; - v22 = input->faces[(fi == flen - 1) ? start : start + fi + 1]; - } - copy_v2db_v2fl(co11, input->vert_coords[v11]); - copy_v2db_v2fl(co12, input->vert_coords[v12]); - copy_v2db_v2fl(co21, input->vert_coords[v21]); - copy_v2db_v2fl(co22, input->vert_coords[v22]); - if (check_vert_near_segment(co11, co21, co22, eps_sq, &lambda)) { - - BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e2, v11, lambda})); - } - if (check_vert_near_segment(co12, co21, co22, eps_sq, &lambda)) { - BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e2, v12, lambda})); - } - if (check_vert_near_segment(co21, co11, co12, eps_sq, &lambda)) { - BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e1, v21, lambda})); - } - if (check_vert_near_segment(co22, co11, co12, eps_sq, &lambda)) { - BLI_array_append(edge_vert_lambda, ((EdgeVertLambda){e1, v22, lambda})); - } - } - } - - evl_len = BLI_array_len(edge_vert_lambda); - if (evl_len > 0) { - /* Sort to bring splits for each edge together, - * and for each edge, to be in order of lambda. */ - qsort(edge_vert_lambda, evl_len, sizeof(EdgeVertLambda), evl_cmp); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\nafter sorting\n"); - for (i = 0; i < evl_len; i++) { - evl = &edge_vert_lambda[i]; - fprintf(stderr, "e%d, v%d, %g\n", evl->e_id, evl->v_id, evl->lambda); - } - } -#endif - - /* Remove dups in edge_vert_lambda, where dup means that the edge is the - * same, and the verts are either the same or will be merged by epsilon-nearness. - */ - i = 0; - j = 0; - /* In loop, copy from position j to position i. */ - for (j = 0; j < evl_len;) { - int k; - if (i != j) { - memmove(&edge_vert_lambda[i], &edge_vert_lambda[j], sizeof(EdgeVertLambda)); - } - for (k = j + 1; k < evl_len; k++) { - int vj = edge_vert_lambda[j].v_id; - int vk = edge_vert_lambda[k].v_id; - if (vj != vk) { - if (len_squared_v2v2(input->vert_coords[vj], input->vert_coords[vk]) > (float)eps_sq) { - break; - } - } - } - j = k; - i++; - } - - if (i != evl_len) { - evl_len = i; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\nduplicates eliminated\n"); - for (i = 0; i < evl_len; i++) { - evl = &edge_vert_lambda[i]; - fprintf(stderr, "e%d, v%d, %g\n", evl->e_id, evl->v_id, evl->lambda); - } - } -#endif - } - /* Find delta in number of constraint edges and face edges. - * This may be overestimates of true number, due to duplicates. */ - delta_con_edges = 0; - delta_face_edges = 0; - cur_e_cnt = 0; - eprev = -1; - for (i = 0; i < evl_len; i++) { - e = edge_vert_lambda[i].e_id; - if (i > 0 && e > eprev) { - /* New edge group. Previous group had cur_e_cnt split vertices. - * That is the delta in the number of edges needed in input since - * there will be cur_e_cnt + 1 edges replacing one edge. - */ - if (eprev < edges_len) { - delta_con_edges += cur_e_cnt; - } - else { - delta_face_edges += cur_e_cnt; - } - cur_e_cnt = 1; - ; - } - else { - cur_e_cnt++; - } - eprev = e; - } - if (eprev < edges_len) { - delta_con_edges += cur_e_cnt; - } - else { - delta_face_edges += cur_e_cnt; - } - new_tot_con_edges = input->edges_len + delta_con_edges; - if (input->faces_len > 0) { - new_tot_face_edges = input->faces_start_table[input->faces_len - 1] + - input->faces_len_table[input->faces_len - 1] + delta_face_edges; - } - else { - new_tot_face_edges = 0; - } - - /* Allocate new CDT_input, now we know sizes needed (perhaps overestimated a bit). - * Caller will be responsible for freeing it and its arrays. - */ - new_input = MEM_callocN(sizeof(CDT_input), __func__); - new_input->epsilon = input->epsilon; - new_input->verts_len = input->verts_len; - new_input->vert_coords = (float(*)[2])MEM_malloc_arrayN( - new_input->verts_len, sizeof(float[2]), __func__); - /* We don't do it now, but may decide to change coords of snapped verts. */ - memmove(new_input->vert_coords, - input->vert_coords, - sizeof(float[2]) * (size_t)new_input->verts_len); - - if (edges_len > 0) { - new_input->edges_len = new_tot_con_edges; - new_input->edges = (int(*)[2])MEM_malloc_arrayN(new_tot_con_edges, sizeof(int[2]), __func__); - } - - if (input->faces_len > 0) { - new_input->faces_len = input->faces_len; - new_input->faces_start_table = (int *)MEM_malloc_arrayN( - new_input->faces_len, sizeof(int), __func__); - new_input->faces_len_table = (int *)MEM_malloc_arrayN( - new_input->faces_len, sizeof(int), __func__); - new_input->faces = (int *)MEM_malloc_arrayN(new_tot_face_edges, sizeof(int), __func__); - } - - edge_map = (int *)MEM_malloc_arrayN( - new_tot_con_edges + new_tot_face_edges, sizeof(int), __func__); - *r_edge_map = edge_map; - - i_new = i_old = i_evl = 0; - e = edge_vert_lambda[0].e_id; - /* First do new constraint edges. */ - for (i_old = 0; i_old < edges_len; i_old++) { - if (i_old < e) { - /* Edge for i_old not split; copy it into new_input. */ - new_input->edges[i_new][0] = input->edges[i_old][0]; - new_input->edges[i_new][1] = input->edges[i_old][1]; - edge_map[i_new] = i_old; - i_new++; - } - else { - /* Edge for i_old is split. */ - BLI_assert(i_old == e); - new_input->edges[i_new][0] = input->edges[i_old][0]; - new_input->edges[i_new][1] = edge_vert_lambda[i_evl].v_id; - edge_map[i_new] = i_old; - i_new++; - i_evl++; - while (i_evl < evl_len && e == edge_vert_lambda[i_evl].e_id) { - new_input->edges[i_new][0] = new_input->edges[i_new - 1][1]; - new_input->edges[i_new][1] = edge_vert_lambda[i_evl].v_id; - edge_map[i_new] = i_old; - i_new++; - i_evl++; - } - new_input->edges[i_new][0] = new_input->edges[i_new - 1][1]; - new_input->edges[i_new][1] = input->edges[i_old][1]; - edge_map[i_new] = i_old; - i_new++; - if (i_evl < evl_len) { - e = edge_vert_lambda[i_evl].e_id; - } - else { - e = INT_MAX; - } - } - } - BLI_assert(i_new <= new_tot_con_edges); - new_input->edges_len = i_new; - - /* Now do face constraints. */ - if (input->faces_len > 0) { - f = 0; - i_new = 0; /* Now will index cur place in new_input->faces. */ - while (i_old < tot_edge_constraints) { - flen = input->faces_len_table[f]; - BLI_assert(i_old - edges_len == input->faces_start_table[f]); - new_input->faces_start_table[f] = i_new; - if (i_old + flen - 1 < e) { - /* Face f is not split. */ - for (j = 0; j < flen; j++) { - new_input->faces[i_new] = input->faces[i_old - edges_len + j]; - edge_map[i_new + new_input->edges_len] = i_old + j; - i_new++; - } - i_old += flen; - new_input->faces_len_table[f] = flen; - f++; - } - else { - /* Face f has at least one split edge. */ - int i_new_start = i_new; - for (j = 0; j < flen; j++) { - if (i_old + j < e) { - /* jth edge of f is not split. */ - new_input->faces[i_new] = input->faces[i_old - edges_len + j]; - edge_map[i_new + new_input->edges_len] = i_old + j; - i_new++; - } - else { - /* jth edge of f is split. */ - BLI_assert(i_old + j == e); - new_input->faces[i_new] = input->faces[i_old - edges_len + j]; - edge_map[i_new + new_input->edges_len] = i_old + j; - i_new++; - while (i_evl < evl_len && e == edge_vert_lambda[i_evl].e_id) { - new_input->faces[i_new] = edge_vert_lambda[i_evl].v_id; - edge_map[i_new + new_input->edges_len] = i_old + j; - i_new++; - i_evl++; - } - if (i_evl < evl_len) { - e = edge_vert_lambda[i_evl].e_id; - } - else { - e = INT_MAX; - } - } - } - new_input->faces_len_table[f] = i_new - i_new_start; - i_old += flen; - f++; - } - } - } - -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\nnew constraint edges\n"); - for (i = 0; i < new_input->edges_len; i++) { - fprintf(stderr, " e%d: (%d,%d)\n", i, new_input->edges[i][0], new_input->edges[i][1]); - } - fprintf(stderr, "\nnew faces\n"); - for (f = 0; f < new_input->faces_len; f++) { - flen = new_input->faces_len_table[f]; - start = new_input->faces_start_table[f]; - fprintf(stderr, " f%d: start=%d, len=%d\n ", f, start, flen); - for (i = start; i < start + flen; i++) { - fprintf(stderr, "%d ", new_input->faces[i]); - } - fprintf(stderr, "\n"); - } - fprintf(stderr, "\nedge map (new->old)\n"); - for (i = 0; i < new_tot_con_edges + new_tot_face_edges; i++) { - fprintf(stderr, " %d->%d\n", i, edge_map[i]); - } - } -#endif - } - - BLI_array_free(edge_vert_lambda); - if (new_input != NULL) { - return (const CDT_input *)new_input; - } - return input; -} - -static void free_modified_input(CDT_input *input) -{ - MEM_freeN(input->vert_coords); - if (input->edges != NULL) { - MEM_freeN(input->edges); - } - if (input->faces != NULL) { - MEM_freeN(input->faces); - MEM_freeN(input->faces_len_table); - MEM_freeN(input->faces_start_table); - } - MEM_freeN(input); -} - -/* Return true if we can merge se's vert into se->next's vert - * without making the area of any new triangle formed by doing - * that into a zero or negative area triangle.*/ -static bool can_collapse(const SymEdge *se) -{ - SymEdge *loop_se; - const double *co = se->next->vert->co; - - for (loop_se = se->rot; loop_se != se && loop_se->rot != se; loop_se = loop_se->rot) { - if (orient2d(co, loop_se->next->vert->co, loop_se->rot->next->vert->co) <= 0.0) { - return false; - } - } - return true; -} - -/* - * Merge one end of e onto the other, fixing up surrounding faces. - * - * General situation looks something like: - * - * c-----e - * / \ / \ - * / \ / \ - * a------b-----f - * \ / \ / - * \ / \ / - * d-----g - * - * where ab is the tiny edge. We want to merge a and b and delete edge ab. - * We don't want to change the coordinates of input vertices [We could revisit this - * in the future, as API def doesn't prohibit this, but callers will appreciate if they - * don't change.] - * Sometimes the collapse shouldn't happen because the triangles formed by the changed - * edges may end up with zero or negative area (see can_collapse, above). - * So don't choose a collapse direction that is not allowed or one that has an original vertex - * as origin and a non-original vertex as destination. - * If both collapse directions are allowed by that rule, pick the one with the lower original - * index. - * - * After merging, the faces abc and adb disappear (if they are not the outer face). - * Suppose we merge b onto a. - * Then edges cb and db are deleted. Face cbe becomes cae and face bdg becomes adg. - * Any other faces attached to b now have a in their place. - * We can do this by rotating edges round b, replacing their vert references with a. - * Similar statements can be made about what happens when a merges into b; - * in code below we'll swap a and b to make above lettering work for a b->a merge. - * Return the vert at the collapsed edge, if a collapse happens. - */ -static CDTVert *collapse_tiny_edge(CDT_state *cdt, CDTEdge *e) -{ - CDTVert *va, *vb; - SymEdge *ab_se, *ba_se, *bd_se, *bc_se, *ad_se, *ac_se; - SymEdge *bg_se, *be_se, *se, *gb_se, *ca_se; - bool can_collapse_a_to_b, can_collapse_b_to_a; -#ifdef DEBUG_CDT - int dbg_level = 0; -#endif - - ab_se = &e->symedges[0]; - ba_se = &e->symedges[1]; - va = ab_se->vert; - vb = ba_se->vert; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "\ncollapse_tiny_edge\n"); - dump_se(&e->symedges[0], "tiny edge"); - fprintf(stderr, "a = [%d], b = [%d]\n", va->index, vb->index); - validate_cdt(cdt, true, false, true); - } -#endif - can_collapse_a_to_b = can_collapse(ab_se); - can_collapse_b_to_a = can_collapse(ba_se); - /* Now swap a and b if necessary and possible, so that from this point on we are collapsing b to - * a. */ - if (va->index > vb->index || !can_collapse_b_to_a) { - if (can_collapse_a_to_b && !(is_original_vert(va, cdt) && !is_original_vert(vb, cdt))) { - SWAP(CDTVert *, va, vb); - ab_se = &e->symedges[1]; - ba_se = &e->symedges[0]; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "swapped a and b\n"); - } -#endif - } - else { - /* Neither collapse direction is OK. */ -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "neither collapse direction ok\n"); - } -#endif - return NULL; - } - } - bc_se = ab_se->next; - bd_se = ba_se->rot; - if (bd_se == ba_se) { - /* Shouldn't happen. Wire edge in outer face. */ - fprintf(stderr, "unexpected wire edge\n"); - return NULL; - } - vb->merge_to_index = va->merge_to_index == -1 ? va->index : va->merge_to_index; - vb->symedge = NULL; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, - "vb = v[%d] merges to va = v[%d], vb->merge_to_index=%d\n", - vb->index, - va->index, - vb->merge_to_index); - } -#endif - /* First fix the vertex of intermediate triangles, like bgf. */ - for (se = bd_se->rot; se != bc_se; se = se->rot) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - dump_se(se, "intermediate tri edge, setting vert to va"); - } -#endif - se->vert = va; - } - ad_se = sym(sym(bd_se)->rot); - ca_se = bc_se->next; - ac_se = sym(ca_se); - if (bd_se->rot != bc_se) { - bg_se = bd_se->rot; - be_se = sym(bc_se)->next; - gb_se = sym(bg_se); - } - else { - bg_se = NULL; - be_se = NULL; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "delete bd, inputs to ad\n"); - dump_se(bd_se, " bd"); - dump_se(ad_se, " ad"); - fprintf(stderr, "delete bc, inputs to ac\n"); - dump_se(bc_se, " bc"); - dump_se(ac_se, " ac"); - fprintf(stderr, "delete ab\n"); - dump_se(ab_se, " ab"); - if (bg_se != NULL) { - fprintf(stderr, "fix up bg, be\n"); - dump_se(bg_se, " bg"); - dump_se(be_se, " be"); - } - } -#endif - add_list_to_input_ids(&ad_se->edge->input_ids, bd_se->edge->input_ids, cdt); - delete_edge(cdt, bd_se); - add_list_to_input_ids(&ac_se->edge->input_ids, bc_se->edge->input_ids, cdt); - delete_edge(cdt, sym(bc_se)); - /* At this point we have this: - * - * c-----e - * / / \ - * / / \ - * a------b-----f - * \ \ / - * \ \ / - * d-----g - * - * Or, if there is not bg_se and be_se, like this: - * - * c - * / - * / - * a------b - * \ - * \ - * d - * - * (But we've already changed the vert field for bg, bf, ..., be to be va.) - */ - if (bg_se != NULL) { - gb_se->next = ad_se; - ad_se->rot = bg_se; - ca_se->next = be_se; - be_se->rot = ac_se; - bg_se->vert = va; - be_se->vert = va; - } - else { - ca_se->next = ad_se; - ad_se->rot = ac_se; - } - /* Don't use delete_edge as it changes too much. */ - ab_se->next = ab_se->rot = NULL; - ba_se->next = ba_se->rot = NULL; - if (va->symedge == ab_se) { - va->symedge = ac_se; - } - return va; -} - -/* - * Check to see if e is tiny (length <= epsilon) and queue it if so. - */ -static void maybe_enqueue_small_feature(CDT_state *cdt, CDTEdge *e, LinkNodePair *tiny_edge_queue) -{ - SymEdge *se, *sesym; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nmaybe_enqueue_small_features\n"); - dump_se(&e->symedges[0], " se0"); - } -#endif - - if (is_deleted_edge(e) || e->in_queue) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "returning because of e conditions\n"); - } -#endif - return; - } - se = &e->symedges[0]; - sesym = &e->symedges[1]; - if (len_squared_v2v2_db(se->vert->co, sesym->vert->co) <= cdt->epsilon_squared) { - BLI_linklist_append_pool(tiny_edge_queue, e, cdt->listpool); - e->in_queue = true; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "Queue tiny edge\n"); - } -#endif - } -} - -/* Consider all edges in rot ring around v for possible enqueing as small features .*/ -static void maybe_enqueue_small_features(CDT_state *cdt, CDTVert *v, LinkNodePair *tiny_edge_queue) -{ - SymEdge *se, *se_start; - - se = se_start = v->symedge; - if (!se_start) { - return; - } - do { - maybe_enqueue_small_feature(cdt, se->edge, tiny_edge_queue); - } while ((se = se->rot) != se_start); -} - -/* Collapse small edges (length <= epsilon) until no more exist. - */ -static void remove_small_features(CDT_state *cdt) -{ - double epsilon = cdt->epsilon; - LinkNodePair tiny_edge_queue = {NULL, NULL}; - BLI_mempool *pool = cdt->listpool; - LinkNode *ln; - CDTEdge *e; - CDTVert *v_change; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nREMOVE_SMALL_FEATURES, epsilon=%g\n", epsilon); - } -#endif - - if (epsilon == 0.0) { - return; - } - - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - maybe_enqueue_small_feature(cdt, e, &tiny_edge_queue); - } - - while (tiny_edge_queue.list != NULL) { - e = (CDTEdge *)BLI_linklist_pop_pool(&tiny_edge_queue.list, pool); - if (tiny_edge_queue.list == NULL) { - tiny_edge_queue.last_node = NULL; - } - e->in_queue = false; - if (is_deleted_edge(e)) { - continue; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "collapse tiny edge\n"); - dump_se(&e->symedges[0], ""); - } -#endif - v_change = collapse_tiny_edge(cdt, e); - if (v_change) { - maybe_enqueue_small_features(cdt, v_change, &tiny_edge_queue); - } - } -} - -/* Remove all non-constraint edges. */ -static void remove_non_constraint_edges(CDT_state *cdt) -{ - LinkNode *ln; - CDTEdge *e; - SymEdge *se; - - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - se = &e->symedges[0]; - if (!is_deleted_edge(e) && !is_constrained_edge(e)) { - dissolve_symedge(cdt, se); - } - } -} - -/* - * Remove the non-constraint edges, but leave enough of them so that all of the - * faces that would be bmesh faces (that is, the faces that have some input representative) - * are valid: they can't have holes, they can't have repeated vertices, and they can't have - * repeated edges. - * - * Not essential, but to make the result look more aesthetically nice, - * remove the edges in order of decreasing length, so that it is more likely that the - * final remaining support edges are short, and therefore likely to make a fairly - * direct path from an outer face to an inner hole face. - */ - -/* For sorting edges by decreasing length (squared). */ -struct EdgeToSort { - double len_squared; - CDTEdge *e; -}; - -static int edge_to_sort_cmp(const void *a, const void *b) -{ - const struct EdgeToSort *e1 = a; - const struct EdgeToSort *e2 = b; - - if (e1->len_squared > e2->len_squared) { - return -1; - } - if (e1->len_squared < e2->len_squared) { - return 1; - } - return 0; -} - -static void remove_non_constraint_edges_leave_valid_bmesh(CDT_state *cdt) -{ - LinkNode *ln; - CDTEdge *e; - SymEdge *se, *se2; - CDTFace *fleft, *fright; - bool dissolve; - size_t nedges; - int i, ndissolvable; - const double *co1, *co2; - struct EdgeToSort *sorted_edges; - - nedges = 0; - for (ln = cdt->edges; ln; ln = ln->next) { - nedges++; - } - if (nedges == 0) { - return; - } - sorted_edges = BLI_memarena_alloc(cdt->arena, nedges * sizeof(*sorted_edges)); - i = 0; - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (!is_deleted_edge(e) && !is_constrained_edge(e)) { - sorted_edges[i].e = e; - co1 = e->symedges[0].vert->co; - co2 = e->symedges[1].vert->co; - sorted_edges[i].len_squared = len_squared_v2v2_db(co1, co2); - i++; - } - } - ndissolvable = i; - qsort(sorted_edges, ndissolvable, sizeof(*sorted_edges), edge_to_sort_cmp); - for (i = 0; i < ndissolvable; i++) { - e = sorted_edges[i].e; - se = &e->symedges[0]; - dissolve = true; - if (true /*!edge_touches_frame(e)*/) { - fleft = se->face; - fright = sym(se)->face; - if (fleft != cdt->outer_face && fright != cdt->outer_face && - (fleft->input_ids != NULL || fright->input_ids != NULL)) { - /* Is there another symedge with same left and right faces? - * Or is there a vertex not part of e touching the same left and right faces? */ - for (se2 = se->next; dissolve && se2 != se; se2 = se2->next) { - if (sym(se2)->face == fright || - (se2->vert != se->next->vert && vert_touches_face(se2->vert, fright))) { - dissolve = false; - } - } - } - } - if (dissolve) { - dissolve_symedge(cdt, se); - } - } -} - -static void remove_outer_edges_until_constraints(CDT_state *cdt) -{ - LinkNode *fstack = NULL; - SymEdge *se, *se_start; - CDTFace *f, *fsym; - int visit = ++cdt->visit_count; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "remove_outer_edges_until_constraints\n"); - } -#endif - - cdt->outer_face->visit_index = visit; - /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */ - se_start = se = cdt->outer_face->symedge; - do { - if (!is_constrained_edge(se->edge)) { - fsym = sym(se)->face; - if (fsym->visit_index != visit) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "pushing f=%p from symedge ", fsym); - dump_se(se, "an outer edge"); - } -#endif - BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool); - } - } - } while ((se = se->next) != se_start); - - while (fstack != NULL) { - LinkNode *to_dissolve = NULL; - bool dissolvable; - f = (CDTFace *)BLI_linklist_pop_pool(&fstack, cdt->listpool); - if (f->visit_index == visit) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "skipping f=%p, already visited\n", f); - } -#endif - continue; - } - BLI_assert(f != cdt->outer_face); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "top of loop, f=%p\n", f); - dump_se_cycle(f->symedge, "visit", 10000); - if (dbg_level > 1) { - dump_cdt(cdt, "cdt at top of loop"); - cdt_draw(cdt, "top of dissolve loop"); - } - } -#endif - f->visit_index = visit; - se_start = se = f->symedge; - do { - dissolvable = !is_constrained_edge(se->edge); -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se(se, "edge in f"); - fprintf(stderr, " dissolvable=%d\n", dissolvable); - } -#endif - if (dissolvable) { - fsym = sym(se)->face; -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_se_cycle(fsym->symedge, "fsym", 10000); - fprintf(stderr, " visited=%d\n", fsym->visit_index == visit); - } -#endif - if (fsym->visit_index != visit) { -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "pushing face %p\n", fsym); - dump_se_cycle(fsym->symedge, "pushed", 10000); - } -#endif - BLI_linklist_prepend_pool(&fstack, fsym, cdt->listpool); - } - else { - BLI_linklist_prepend_pool(&to_dissolve, se, cdt->listpool); - } - } - se = se->next; - } while (se != se_start); - while (to_dissolve != NULL) { - se = (SymEdge *)BLI_linklist_pop_pool(&to_dissolve, cdt->listpool); - if (se->next != NULL) { - dissolve_symedge(cdt, se); - } - } - } -} - -/** - * Remove edges and merge faces to get desired output, as per options. - * \note the cdt cannot be further changed after this. - */ -static void prepare_cdt_for_output(CDT_state *cdt, const CDT_output_type output_type) -{ - CDTFace *f; - CDTEdge *e; - LinkNode *ln; - - cdt->output_prepared = true; - if (cdt->edges == NULL) { - return; - } - - /* Make sure all non-deleted faces have a symedge. */ - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (!is_deleted_edge(e)) { - if (e->symedges[0].face->symedge == NULL) { - e->symedges[0].face->symedge = &e->symedges[0]; - } - if (e->symedges[1].face->symedge == NULL) { - e->symedges[1].face->symedge = &e->symedges[1]; - } - } - } -#ifdef DEBUG_CDT - /* All non-deleted faces should have a symedge now. */ - for (ln = cdt->faces; ln; ln = ln->next) { - f = (CDTFace *)ln->link; - if (!f->deleted) { - BLI_assert(f->symedge != NULL); - } - } -#else - UNUSED_VARS(f); -#endif - - if (output_type == CDT_CONSTRAINTS) { - remove_non_constraint_edges(cdt); - } - else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) { - remove_non_constraint_edges_leave_valid_bmesh(cdt); - } - else if (output_type == CDT_INSIDE) { - remove_outer_edges_until_constraints(cdt); - } -} - -static CDT_result *cdt_get_output(CDT_state *cdt, - const CDT_input *input, - const CDT_output_type output_type) -{ - int i, j, nv, ne, nf, faces_len_total; - int orig_map_size, orig_map_index; - int *vert_to_output_map; - CDT_result *result; - CDTVert *v; - LinkNode *lne, *lnf, *ln; - SymEdge *se, *se_start; - CDTEdge *e; - CDTFace *f; -#ifdef DEBUG_CDT - int dbg_level = 0; - - if (dbg_level > 0) { - fprintf(stderr, "\nCDT_GET_OUTPUT\n\n"); - } -#endif - - prepare_cdt_for_output(cdt, output_type); - - result = (CDT_result *)MEM_callocN(sizeof(*result), __func__); - if (cdt->vert_array_len == 0) { - return result; - } - -#ifdef DEBUG_CDT - if (dbg_level > 1) { - dump_cdt(cdt, "cdt to output"); - } -#endif - - /* All verts without a merge_to_index will be output. - * vert_to_output_map[i] will hold the output vertex index - * corresponding to the vert in position i in cdt->vert_array. - * Since merging picked the leftmost-lowermost representative, - * that is not necessarily the same as the vertex with the lowest original - * index (i.e., index in cdt->vert_array), so we need two passes: - * one to get the non-merged-to vertices in vert_to_output_map, - * and a second to put in the merge targets for merged-to vertices. - */ - vert_to_output_map = BLI_memarena_alloc(cdt->arena, (size_t)cdt->vert_array_len * sizeof(int *)); - nv = 0; - for (i = 0; i < cdt->vert_array_len; i++) { - v = cdt->vert_array[i]; - if (v->merge_to_index == -1) { - vert_to_output_map[i] = nv; - nv++; - } - } - if (nv <= 0) { - return result; - } - if (nv < cdt->vert_array_len) { - for (i = 0; i < input->verts_len; i++) { - v = cdt->vert_array[i]; - if (v->merge_to_index != -1) { - add_to_input_ids(&cdt->vert_array[v->merge_to_index]->input_ids, i, cdt); - vert_to_output_map[i] = vert_to_output_map[v->merge_to_index]; - } - } - } - - result->verts_len = nv; - result->vert_coords = MEM_malloc_arrayN(nv, sizeof(result->vert_coords[0]), __func__); - - /* Make the vertex "orig" map arrays, mapping output verts to lists of input ones. */ - orig_map_size = 0; - for (i = 0; i < cdt->vert_array_len; i++) { - if (cdt->vert_array[i]->merge_to_index == -1) { - orig_map_size += 1 + BLI_linklist_count(cdt->vert_array[i]->input_ids); - } - } - result->verts_orig_len_table = MEM_malloc_arrayN(nv, sizeof(int), __func__); - result->verts_orig_start_table = MEM_malloc_arrayN(nv, sizeof(int), __func__); - result->verts_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); - - orig_map_index = 0; - i = 0; - for (j = 0; j < cdt->vert_array_len; j++) { - v = cdt->vert_array[j]; - if (v->merge_to_index == -1) { - result->vert_coords[i][0] = (float)v->co[0]; - result->vert_coords[i][1] = (float)v->co[1]; - result->verts_orig_start_table[i] = orig_map_index; - if (j < input->verts_len) { - result->verts_orig[orig_map_index++] = j; - } - for (ln = v->input_ids; ln; ln = ln->next) { - result->verts_orig[orig_map_index++] = POINTER_AS_INT(ln->link); - } - result->verts_orig_len_table[i] = orig_map_index - result->verts_orig_start_table[i]; - i++; - } - } - - ne = 0; - orig_map_size = 0; - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (!is_deleted_edge(e)) { - ne++; - if (e->input_ids) { - orig_map_size += BLI_linklist_count(e->input_ids); - } - } - } - if (ne != 0) { - result->edges_len = ne; - result->face_edge_offset = cdt->face_edge_offset; - result->edges = MEM_malloc_arrayN(ne, sizeof(result->edges[0]), __func__); - result->edges_orig_len_table = MEM_malloc_arrayN(ne, sizeof(int), __func__); - result->edges_orig_start_table = MEM_malloc_arrayN(ne, sizeof(int), __func__); - if (orig_map_size > 0) { - result->edges_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); - } - orig_map_index = 0; - i = 0; - for (lne = cdt->edges; lne; lne = lne->next) { - e = (CDTEdge *)lne->link; - if (!is_deleted_edge(e)) { - result->edges[i][0] = vert_to_output_map[e->symedges[0].vert->index]; - result->edges[i][1] = vert_to_output_map[e->symedges[1].vert->index]; - result->edges_orig_start_table[i] = orig_map_index; - for (ln = e->input_ids; ln; ln = ln->next) { - result->edges_orig[orig_map_index++] = POINTER_AS_INT(ln->link); - } - result->edges_orig_len_table[i] = orig_map_index - result->edges_orig_start_table[i]; - i++; - } - } - } - - nf = 0; - faces_len_total = 0; - orig_map_size = 0; - for (ln = cdt->faces; ln; ln = ln->next) { - f = (CDTFace *)ln->link; - if (!f->deleted && f != cdt->outer_face) { - nf++; - se = se_start = f->symedge; - BLI_assert(se != NULL); - do { - faces_len_total++; - se = se->next; - } while (se != se_start); - if (f->input_ids) { - orig_map_size += BLI_linklist_count(f->input_ids); - } - } - } - - if (nf != 0) { - result->faces_len = nf; - result->faces_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); - result->faces_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); - result->faces = MEM_malloc_arrayN(faces_len_total, sizeof(int), __func__); - result->faces_orig_len_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); - result->faces_orig_start_table = MEM_malloc_arrayN(nf, sizeof(int), __func__); - if (orig_map_size > 0) { - result->faces_orig = MEM_malloc_arrayN(orig_map_size, sizeof(int), __func__); - } - orig_map_index = 0; - i = 0; - j = 0; - for (lnf = cdt->faces; lnf; lnf = lnf->next) { - f = (CDTFace *)lnf->link; - if (!f->deleted && f != cdt->outer_face) { - result->faces_start_table[i] = j; - se = se_start = f->symedge; - do { - result->faces[j++] = vert_to_output_map[se->vert->index]; - se = se->next; - } while (se != se_start); - result->faces_len_table[i] = j - result->faces_start_table[i]; - result->faces_orig_start_table[i] = orig_map_index; - for (ln = f->input_ids; ln; ln = ln->next) { - result->faces_orig[orig_map_index++] = POINTER_AS_INT(ln->link); - } - result->faces_orig_len_table[i] = orig_map_index - result->faces_orig_start_table[i]; - i++; - } - } - } - return result; -} - -/** - * Calculate the Constrained Delaunay Triangulation of the 2d elements given in \a input. - * - * A Delaunay triangulation of a set of vertices is a triangulation where no triangle in the - * triangulation has a circumcircle that strictly contains another vertex. Delaunay triangulations - * are avoid long skinny triangles: they maximize the minimum angle of all triangles in the - * triangulation. - * - * A Constrained Delaunay Triangulation adds the requirement that user-provided line segments must - * appear as edges in the output (perhaps divided into several sub-segments). It is not required - * that the input edges be non-intersecting: this routine will calculate the intersections. This - * means that besides triangulating, this routine is also useful for general and robust 2d edge and - * face intersection. - * - * This routine also takes an epsilon parameter in the \a input. Input vertices closer than epsilon - * will be merged, and we collapse tiny edges (less than epsilon length). - * - * The current initial Deluanay triangulation algorithm is the Guibas-Stolfi Divide and Conquer - * algorithm (see "Primitives for the Manipulation of General Subdivisions and the Computation of - * Voronoi Diagrams"). and uses Shewchuk's exact predicates to issues where numeric errors cause - * inconsistent geometric judgments. This is followed by inserting edge constraints (including the - * edges implied by faces) using the algorithms discussed in "Fully Dynamic Constrained Delaunay - * Triangulations" by Kallmann, Bieri, and Thalmann. - * - * \param input: points to a CDT_input struct which contains the vertices, edges, and faces to be - * triangulated. \param output_type: specifies which edges to remove after doing the triangulation. - * \return A pointer to an allocated CDT_result struct, which describes the triangulation in terms - * of vertices, edges, and faces, and also has tables to map output elements back to input - * elements. The caller must use BLI_delaunay_2d_cdt_free() on the result when done with it. - * - * See the header file BLI_delaunay_2d.h for details of the CDT_input and CDT_result structs and - * the CDT_output_type enum. - */ -CDT_result *BLI_delaunay_2d_cdt_calc(const CDT_input *input, const CDT_output_type output_type) -{ - int nv = input->verts_len; - int ne = input->edges_len; - int nf = input->faces_len; - int i, iv1, iv2, f, fedge_start, fedge_end, ei; - CDT_state *cdt; - CDTVert *v1, *v2; - CDTEdge *face_edge; - SymEdge *face_symedge; - LinkNode *edge_list; - CDT_result *result; - const CDT_input *input_orig; - int *new_edge_map; - static bool called_exactinit = false; -#ifdef DEBUG_CDT - int dbg_level = 0; -#endif - - /* The exact orientation and incircle primitives need a one-time initialization of certain - * constants. */ - if (!called_exactinit) { - exactinit(); - called_exactinit = true; - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, - "\n\nCDT CALC, nv=%d, ne=%d, nf=%d, eps=%g\n", - input->verts_len, - input->edges_len, - input->faces_len, - input->epsilon); - } - if (dbg_level == -1) { - write_cdt_input_to_file(input); - } -#endif - - if ((nv > 0 && input->vert_coords == NULL) || (ne > 0 && input->edges == NULL) || - (nf > 0 && (input->faces == NULL || input->faces_start_table == NULL || - input->faces_len_table == NULL))) { -#ifdef DEBUG_CDT - fprintf(stderr, "invalid input: unexpected NULL array(s)\n"); -#endif - return NULL; - } - - input_orig = input; - input = modify_input_for_near_edge_ends(input, &new_edge_map); - if (input != input_orig) { - nv = input->verts_len; - ne = input->edges_len; - nf = input->faces_len; -#ifdef DEBUG_CDT - if (dbg_level > 0) { - fprintf(stderr, "input modified for near ends; now ne=%d\n", ne); - } -#endif - } - cdt = cdt_init(input); - initial_triangulation(cdt); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - validate_cdt(cdt, true, false, false); - if (dbg_level > 1) { - cdt_draw(cdt, "after initial triangulation"); - } - } -#endif - - for (i = 0; i < ne; i++) { - iv1 = input->edges[i][0]; - iv2 = input->edges[i][1]; - if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) { -#ifdef DEBUG_CDT - fprintf(stderr, "edge indices for e%d not valid: v1=%d, v2=%d, nv=%d\n", i, iv1, iv2, nv); -#endif - continue; - } - v1 = cdt->vert_array[iv1]; - v2 = cdt->vert_array[iv2]; - if (v1->merge_to_index != -1) { - v1 = cdt->vert_array[v1->merge_to_index]; - } - if (v2->merge_to_index != -1) { - v2 = cdt->vert_array[v2->merge_to_index]; - } - if (new_edge_map) { - ei = new_edge_map[i]; - } - else { - ei = i; - } - add_edge_constraint(cdt, v1, v2, ei, NULL); -#ifdef DEBUG_CDT - if (dbg_level > 3) { - char namebuf[60]; - sprintf(namebuf, "after edge constraint %d = (%d,%d)\n", i, iv1, iv2); - cdt_draw(cdt, namebuf); - // dump_cdt(cdt, namebuf); - validate_cdt(cdt, true, true, false); - } -#endif - } - - cdt->face_edge_offset = ne; - for (f = 0; f < nf; f++) { - int flen = input->faces_len_table[f]; - int fstart = input->faces_start_table[f]; - if (flen <= 2) { -#ifdef DEBUG_CDT - fprintf(stderr, "face %d has length %d; ignored\n", f, flen); -#endif - continue; - } - for (i = 0; i < flen; i++) { - int face_edge_id = cdt->face_edge_offset + fstart + i; - if (new_edge_map) { - face_edge_id = new_edge_map[face_edge_id]; - } - iv1 = input->faces[fstart + i]; - iv2 = input->faces[fstart + ((i + 1) % flen)]; - if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) { -#ifdef DEBUG_CDT - fprintf(stderr, "face indices not valid: f=%d, iv1=%d, iv2=%d, nv=%d\n", f, iv1, iv2, nv); -#endif - continue; - } - v1 = cdt->vert_array[iv1]; - v2 = cdt->vert_array[iv2]; - if (v1->merge_to_index != -1) { - v1 = cdt->vert_array[v1->merge_to_index]; - } - if (v2->merge_to_index != -1) { - v2 = cdt->vert_array[v2->merge_to_index]; - } - add_edge_constraint(cdt, v1, v2, face_edge_id, &edge_list); -#ifdef DEBUG_CDT - if (dbg_level > 2) { - fprintf(stderr, "edges for edge %d:\n", i); - for (LinkNode *ln = edge_list; ln; ln = ln->next) { - CDTEdge *cdt_e = (CDTEdge *)ln->link; - fprintf(stderr, - " (%.2f,%.2f)->(%.2f,%.2f)\n", - F2(cdt_e->symedges[0].vert->co), - F2(cdt_e->symedges[1].vert->co)); - } - } - if (dbg_level > 2) { - cdt_draw(cdt, "after a face edge"); - if (dbg_level > 3) { - dump_cdt(cdt, "after a face edge"); - } - validate_cdt(cdt, true, true, false); - } -#endif - if (i == 0) { - face_edge = (CDTEdge *)edge_list->link; - face_symedge = &face_edge->symedges[0]; - if (face_symedge->vert != v1) { - face_symedge = &face_edge->symedges[1]; - BLI_assert(face_symedge->vert == v1); - } - } - BLI_linklist_free_pool(edge_list, NULL, cdt->listpool); - } - fedge_start = cdt->face_edge_offset + fstart; - fedge_end = fedge_start + flen - 1; - add_face_ids(cdt, face_symedge, f, fedge_start, fedge_end); - } -#ifdef DEBUG_CDT - if (dbg_level > 0) { - validate_cdt(cdt, true, true, false); - } - if (dbg_level > 1) { - cdt_draw(cdt, "after adding edges and faces"); - if (dbg_level > 2) { - dump_cdt(cdt, "after adding edges and faces"); - } - } -#endif - - if (cdt->epsilon > 0.0) { - remove_small_features(cdt); -#ifdef DEBUG_CDT - if (dbg_level > 2) { - cdt_draw(cdt, "after remove small features\n"); - if (dbg_level > 3) { - dump_cdt(cdt, "after remove small features\n"); - } - } -#endif - } - - result = cdt_get_output(cdt, input, output_type); -#ifdef DEBUG_CDT - if (dbg_level > 0) { - cdt_draw(cdt, "final"); - } -#endif - - if (input != input_orig) { - free_modified_input((CDT_input *)input); - } - new_cdt_free(cdt); - return result; -} - -void BLI_delaunay_2d_cdt_free(CDT_result *result) -{ - if (result == NULL) { - return; - } - if (result->vert_coords) { - MEM_freeN(result->vert_coords); - } - if (result->edges) { - MEM_freeN(result->edges); - } - if (result->faces) { - MEM_freeN(result->faces); - } - if (result->faces_start_table) { - MEM_freeN(result->faces_start_table); - } - if (result->faces_len_table) { - MEM_freeN(result->faces_len_table); - } - if (result->verts_orig) { - MEM_freeN(result->verts_orig); - } - if (result->verts_orig_start_table) { - MEM_freeN(result->verts_orig_start_table); - } - if (result->verts_orig_len_table) { - MEM_freeN(result->verts_orig_len_table); - } - if (result->edges_orig) { - MEM_freeN(result->edges_orig); - } - if (result->edges_orig_start_table) { - MEM_freeN(result->edges_orig_start_table); - } - if (result->edges_orig_len_table) { - MEM_freeN(result->edges_orig_len_table); - } - if (result->faces_orig) { - MEM_freeN(result->faces_orig); - } - if (result->faces_orig_start_table) { - MEM_freeN(result->faces_orig_start_table); - } - if (result->faces_orig_len_table) { - MEM_freeN(result->faces_orig_len_table); - } - MEM_freeN(result); -} - -#ifdef DEBUG_CDT - -ATTU static const char *vertname(const CDTVert *v) -{ - static char vertnamebuf[20]; - - sprintf(vertnamebuf, "[%d]", v->index); - return vertnamebuf; -} - -ATTU static const char *sename(const SymEdge *se) -{ - static char senamebuf[20]; - - sprintf(senamebuf, "{%x}", (POINTER_AS_UINT(se)) & 0xFFFF); - return senamebuf; -} - -static void dump_v(const CDTVert *v, const char *lab) -{ - fprintf(stderr, "%s%s(%.10f,%.10f)\n", lab, vertname(v), F2(v->co)); -} - -static void dump_se(const SymEdge *se, const char *lab) -{ - if (se->next) { - fprintf(stderr, - "%s%s((%.10f,%.10f)->(%.10f,%.10f))", - lab, - vertname(se->vert), - F2(se->vert->co), - F2(se->next->vert->co)); - fprintf(stderr, "%s\n", vertname(se->next->vert)); - } - else { - fprintf(stderr, "%s%s((%.10f,%.10f)->NULL)\n", lab, vertname(se->vert), F2(se->vert->co)); - } -} - -static void dump_se_short(const SymEdge *se, const char *lab) -{ - if (se == NULL) { - fprintf(stderr, "%sNULL", lab); - } - else { - fprintf(stderr, "%s%s", lab, vertname(se->vert)); - fprintf(stderr, "%s", se->next == NULL ? "[NULL]" : vertname(se->next->vert)); - } -} - -static void dump_se_cycle(const SymEdge *se, const char *lab, const int limit) -{ - int count = 0; - const SymEdge *s = se; - fprintf(stderr, "%s:\n", lab); - do { - dump_se(s, " "); - s = s->next; - count++; - } while (s != se && count < limit); - if (count == limit) { - fprintf(stderr, " limit hit without cycle!\n"); - } -} - -static void dump_id_list(const LinkNode *id_list, const char *lab) -{ - const LinkNode *ln; - if (!id_list) { - return; - } - fprintf(stderr, "%s", lab); - for (ln = id_list; ln; ln = ln->next) { - fprintf(stderr, "%d%c", POINTER_AS_INT(ln->link), ln->next ? ' ' : '\n'); - } -} - -static void dump_cross_data(struct CrossData *cd, const char *lab) -{ - fprintf(stderr, "%s", lab); - if (cd->lambda == 0.0) { - fprintf(stderr, "v%d", cd->vert->index); - } - else { - fprintf(stderr, "lambda=%.17g", cd->lambda); - } - dump_se_short(cd->in, " in="); - dump_se_short(cd->out, " out="); - fprintf(stderr, "\n"); -} - -/* If filter_fn != NULL, only dump vert v its edges when filter_fn(cdt, v, filter_data) is true. */ -# define PL(p) (POINTER_AS_UINT(p) & 0xFFFF) -static void dump_cdt_filtered(const CDT_state *cdt, - bool (*filter_fn)(const CDT_state *, int, void *), - void *filter_data, - const char *lab) -{ - LinkNode *ln; - CDTVert *v, *vother; - CDTEdge *e; - CDTFace *f; - SymEdge *se; - int i, cnt; - - fprintf(stderr, "\nCDT %s\n", lab); - fprintf(stderr, "\nVERTS\n"); - for (i = 0; i < cdt->vert_array_len; i++) { - if (filter_fn && !filter_fn(cdt, i, filter_data)) { - continue; - } - v = cdt->vert_array[i]; - fprintf(stderr, "%s %x: (%f,%f) symedge=%x", vertname(v), PL(v), F2(v->co), PL(v->symedge)); - if (v->merge_to_index == -1) { - fprintf(stderr, "\n"); - } - else { - fprintf(stderr, " merge to %s\n", vertname(cdt->vert_array[v->merge_to_index])); - continue; - } - dump_id_list(v->input_ids, " "); - se = v->symedge; - cnt = 0; - if (se) { - fprintf(stderr, " edges out:\n"); - do { - if (se->next == NULL) { - fprintf(stderr, " [NULL next/rot symedge, se=%x\n", PL(se)); - break; - } - if (se->next->next == NULL) { - fprintf(stderr, " [NULL next-next/rot symedge, se=%x\n", PL(se)); - break; - } - vother = sym(se)->vert; - fprintf(stderr, " %s (e=%x, se=%x)\n", vertname(vother), PL(se->edge), PL(se)); - se = se->rot; - cnt++; - } while (se != v->symedge && cnt < 25); - fprintf(stderr, "\n"); - } - } - if (filter_fn) { - return; - } - fprintf(stderr, "\nEDGES\n"); - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (e->symedges[0].next == NULL) { - continue; - } - fprintf(stderr, "%x:\n", PL(e)); - for (i = 0; i < 2; i++) { - se = &e->symedges[i]; - fprintf(stderr, - " se[%d] @%x: next=%x, rot=%x, vert=%x [%s] (%.2f,%.2f), edge=%x, face=%x\n", - i, - PL(se), - PL(se->next), - PL(se->rot), - PL(se->vert), - vertname(se->vert), - F2(se->vert->co), - PL(se->edge), - PL(se->face)); - } - dump_id_list(e->input_ids, " "); - } - fprintf(stderr, "\nFACES\n"); - for (ln = cdt->faces; ln; ln = ln->next) { - f = (CDTFace *)ln->link; - if (f->deleted) { - continue; - } - if (f == cdt->outer_face) { - fprintf(stderr, "%x: outer", PL(f)); - } - fprintf(stderr, " symedge=%x\n", PL(f->symedge)); - dump_id_list(f->input_ids, " "); - } - fprintf(stderr, "\nOTHER\n"); - fprintf(stderr, "outer_face=%x\n", PL(cdt->outer_face)); - fprintf( - stderr, "minx=%f, maxx=%f, miny=%f, maxy=%f\n", cdt->minx, cdt->maxx, cdt->miny, cdt->maxy); - fprintf(stderr, "margin=%f\n", cdt->margin); -} -# undef PL - -static void dump_cdt(const CDT_state *cdt, const char *lab) -{ - dump_cdt_filtered(cdt, NULL, NULL, lab); -} - -typedef struct ReachableFilterData { - int vstart_index; - int maxdist; -} ReachableFilterData; - -/* Stupid algorithm will repeat itself. Don't use for large n. */ -static bool reachable_filter(const CDT_state *cdt, int v_index, void *filter_data) -{ - CDTVert *v; - SymEdge *se; - ReachableFilterData *rfd_in = (ReachableFilterData *)filter_data; - ReachableFilterData rfd_next; - - if (v_index == rfd_in->vstart_index) { - return true; - } - if (rfd_in->maxdist <= 0 || v_index < 0 || v_index >= cdt->vert_array_len) { - return false; - } - else { - v = cdt->vert_array[v_index]; - se = v->symedge; - if (se != NULL) { - rfd_next.vstart_index = rfd_in->vstart_index; - rfd_next.maxdist = rfd_in->maxdist - 1; - do { - if (reachable_filter(cdt, se->next->vert->index, &rfd_next)) { - return true; - } - se = se->rot; - } while (se != v->symedge); - } - } - return false; -} - -static void set_min_max(CDT_state *cdt) -{ - int i; - double minx, maxx, miny, maxy; - double *co; - - minx = miny = DBL_MAX; - maxx = maxy = -DBL_MAX; - for (i = 0; i < cdt->vert_array_len; i++) { - co = cdt->vert_array[i]->co; - if (co[0] < minx) { - minx = co[0]; - } - if (co[0] > maxx) { - maxx = co[0]; - } - if (co[1] < miny) { - miny = co[1]; - } - if (co[1] > maxy) { - maxy = co[1]; - } - } - if (minx != DBL_MAX) { - cdt->minx = minx; - cdt->miny = miny; - cdt->maxx = maxx; - cdt->maxy = maxy; - } -} - -static void dump_cdt_vert_neighborhood(CDT_state *cdt, int v, int maxdist, const char *lab) -{ - ReachableFilterData rfd; - rfd.vstart_index = v; - rfd.maxdist = maxdist; - dump_cdt_filtered(cdt, reachable_filter, &rfd, lab); -} - -/* - * Make an html file with svg in it to display the argument cdt. - * Mouse-overs will reveal the coordinates of vertices and edges. - * Constraint edges are drawn thicker than non-constraint edges. - * The first call creates DRAWFILE; subsequent calls append to it. - */ -# define DRAWFILE "/tmp/debug_draw.html" -# define MAX_DRAW_WIDTH 2000 -# define MAX_DRAW_HEIGHT 1400 -# define THIN_LINE 1 -# define THICK_LINE 4 -# define VERT_RADIUS 3 -# define DRAW_VERT_LABELS 1 -# define DRAW_EDGE_LABELS 0 - -static void cdt_draw_region( - CDT_state *cdt, const char *lab, double minx, double miny, double maxx, double maxy) -{ - static bool append = false; - FILE *f = fopen(DRAWFILE, append ? "a" : "w"); - int view_width, view_height; - double width, height, aspect, scale; - LinkNode *ln; - CDTVert *v, *u; - CDTEdge *e; - int i, strokew; - - width = maxx - minx; - height = maxy - miny; - aspect = height / width; - view_width = MAX_DRAW_WIDTH; - view_height = (int)(view_width * aspect); - if (view_height > MAX_DRAW_HEIGHT) { - view_height = MAX_DRAW_HEIGHT; - view_width = (int)(view_height / aspect); - } - scale = view_width / width; - -# define SX(x) ((x - minx) * scale) -# define SY(y) ((maxy - y) * scale) - - if (!f) { - printf("couldn't open file %s\n", DRAWFILE); - return; - } - fprintf(f, "<div>%s</div>\n<div>\n", lab); - fprintf(f, - "<svg version=\"1.1\" " - "xmlns=\"http://www.w3.org/2000/svg\" " - "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " - "xml:space=\"preserve\"\n"); - fprintf(f, "width=\"%d\" height=\"%d\">/n", view_width, view_height); - - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - if (is_deleted_edge(e)) { - continue; - } - u = e->symedges[0].vert; - v = e->symedges[1].vert; - strokew = is_constrained_edge(e) ? THICK_LINE : THIN_LINE; - fprintf(f, - "<line fill=\"none\" stroke=\"black\" stroke-width=\"%d\" " - "x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\">\n", - strokew, - SX(u->co[0]), - SY(u->co[1]), - SX(v->co[0]), - SY(v->co[1])); - fprintf(f, " <title>%s", vertname(u)); - fprintf(f, "%s</title>\n", vertname(v)); - fprintf(f, "</line>\n"); -# if DRAW_EDGE_LABELS - fprintf(f, - "<text x=\"%f\" y=\"%f\" font-size=\"small\">", - SX(0.5 * (u->co[0] + v->co[0])), - SY(0.5 * (u->co[1] + v->co[1]))); - fprintf(f, "%s", vertname(u)); - fprintf(f, "%s", vertname(v)); - fprintf(f, "%s", sename(&e->symedges[0])); - fprintf(f, "%s</text>\n", sename(&e->symedges[1])); -# endif - } - i = 0; - for (; i < cdt->vert_array_len; i++) { - v = cdt->vert_array[i]; - if (v->merge_to_index != -1) { - continue; - } - fprintf(f, - "<circle fill=\"black\" cx=\"%f\" cy=\"%f\" r=\"%d\">\n", - SX(v->co[0]), - SY(v->co[1]), - VERT_RADIUS); - fprintf(f, " <title>%s(%.10f,%.10f)</title>\n", vertname(v), v->co[0], v->co[1]); - fprintf(f, "</circle>\n"); -# if DRAW_VERT_LABELS - fprintf(f, - "<text x=\"%f\" y=\"%f\" font-size=\"small\">%s</text>\n", - SX(v->co[0]) + VERT_RADIUS, - SY(v->co[1]) - VERT_RADIUS, - vertname(v)); -# endif - } - - fprintf(f, "</svg>\n</div>\n"); - fclose(f); - append = true; -# undef SX -# undef SY -} - -static void cdt_draw(CDT_state *cdt, const char *lab) -{ - double draw_margin, minx, maxx, miny, maxy; - - set_min_max(cdt); - draw_margin = (cdt->maxx - cdt->minx + cdt->maxy - cdt->miny + 1) * 0.05; - minx = cdt->minx - draw_margin; - maxx = cdt->maxx + draw_margin; - miny = cdt->miny - draw_margin; - maxy = cdt->maxy + draw_margin; - cdt_draw_region(cdt, lab, minx, miny, maxx, maxy); -} - -static void cdt_draw_vertex_region(CDT_state *cdt, int v, double dist, const char *lab) -{ - const double *co = cdt->vert_array[v]->co; - cdt_draw_region(cdt, lab, co[0] - dist, co[1] - dist, co[0] + dist, co[1] + dist); -} - -static void cdt_draw_edge_region(CDT_state *cdt, int v1, int v2, double dist, const char *lab) -{ - const double *co1 = cdt->vert_array[v1]->co; - const double *co2 = cdt->vert_array[v2]->co; - double minx, miny, maxx, maxy; - - minx = min_dd(co1[0], co2[0]); - miny = min_dd(co1[1], co2[1]); - maxx = max_dd(co1[0], co2[0]); - maxy = max_dd(co1[1], co2[1]); - cdt_draw_region(cdt, lab, minx - dist, miny - dist, maxx + dist, maxy + dist); -} - -# define CDTFILE "/tmp/cdtinput.txt" -static void write_cdt_input_to_file(const CDT_input *inp) -{ - int i, j; - FILE *f = fopen(CDTFILE, "w"); - - fprintf(f, "%d %d %d\n", inp->verts_len, inp->edges_len, inp->faces_len); - for (i = 0; i < inp->verts_len; i++) { - fprintf(f, "%.17f %.17f\n", inp->vert_coords[i][0], inp->vert_coords[i][1]); - } - for (i = 0; i < inp->edges_len; i++) { - fprintf(f, "%d %d\n", inp->edges[i][0], inp->edges[i][1]); - } - for (i = 0; i < inp->faces_len; i++) { - for (j = 0; j < inp->faces_len_table[i]; j++) { - fprintf(f, "%d ", inp->faces[j + inp->faces_start_table[i]]); - } - fprintf(f, "\n"); - } - fclose(f); -} - -# ifndef NDEBUG /* Only used in assert. */ -/* - * Is a visible from b: i.e., ab crosses no edge of cdt? - * If constrained is true, consider only constrained edges as possible crossed edges. - * In any case, don't count an edge ab itself. - * Note: this is an expensive test if there are a lot of edges. - */ -static bool is_visible(const CDTVert *a, const CDTVert *b, bool constrained, const CDT_state *cdt) -{ - const LinkNode *ln; - const CDTEdge *e; - const SymEdge *se, *senext; - double lambda, mu; - int ikind; - - for (ln = cdt->edges; ln; ln = ln->next) { - e = (const CDTEdge *)ln->link; - if (is_deleted_edge(e) || is_border_edge(e, cdt)) { - continue; - } - if (constrained && !is_constrained_edge(e)) { - continue; - } - se = (const SymEdge *)&e->symedges[0]; - senext = se->next; - if ((a == se->vert || a == senext->vert) || b == se->vert || b == se->next->vert) { - continue; - } - ikind = isect_seg_seg_v2_lambda_mu_db( - a->co, b->co, se->vert->co, senext->vert->co, &lambda, &mu); - if (ikind != ISECT_LINE_LINE_NONE) { - if (ikind == ISECT_LINE_LINE_COLINEAR) { - /* TODO: special test here for overlap. */ - continue; - } - /* Allow an intersection very near or at ends, to allow for numerical error. */ - if (lambda > FLT_EPSILON && (1.0 - lambda) > FLT_EPSILON && mu > FLT_EPSILON && - (1.0 - mu) > FLT_EPSILON) { - return false; - } - } - } - return true; -} -# endif - -# ifndef NDEBUG /* Only used in assert. */ -/* - * Check that edge ab satisfies constrained delaunay condition: - * That is, for all non-constraint, non-border edges ab, - * (1) ab is visible in the constraint graph; and - * (2) there is a circle through a and b such that any vertex v connected by an edge to a or b - * is not inside that circle. - * The argument 'se' specifies ab by: a is se's vert and b is se->next's vert. - * Return true if check is OK. - */ -static bool is_delaunay_edge(const SymEdge *se) -{ - int i; - CDTVert *a, *b, *c; - const SymEdge *sesym, *curse, *ss; - bool ok[2]; - - if (!is_constrained_edge(se->edge)) { - return true; - } - sesym = sym(se); - a = se->vert; - b = se->next->vert; - /* Try both the triangles adjacent to se's edge for circle. */ - for (i = 0; i < 2; i++) { - ok[i] = true; - curse = (i == 0) ? se : sesym; - a = curse->vert; - b = curse->next->vert; - c = curse->next->next->vert; - for (ss = curse->rot; ss != curse; ss = ss->rot) { - ok[i] |= incircle(a->co, b->co, c->co, ss->next->vert->co) <= 0.0; - } - } - return ok[0] || ok[1]; -} -# endif - -# ifndef NDEBUG -static bool plausible_non_null_ptr(void *p) -{ - return p > (void *)0x1000; -} -# endif - -static void validate_cdt(CDT_state *cdt, - bool check_all_tris, - bool check_delaunay, - bool check_visibility) -{ - LinkNode *ln; - int totedges, totfaces, totverts; - CDTEdge *e; - SymEdge *se, *sesym, *s; - CDTVert *v, *v1, *v2, *v3; - CDTFace *f; - int i, limit; - bool isborder; - - if (cdt->output_prepared) { - return; - } - if (cdt->edges == NULL || cdt->edges->next == NULL) { - return; - } - - BLI_assert(cdt != NULL); - totedges = 0; - for (ln = cdt->edges; ln; ln = ln->next) { - e = (CDTEdge *)ln->link; - se = &e->symedges[0]; - sesym = &e->symedges[1]; - if (is_deleted_edge(e)) { - BLI_assert(se->rot == NULL && sesym->next == NULL && sesym->rot == NULL); - continue; - } - totedges++; - isborder = is_border_edge(e, cdt); - BLI_assert(se->vert != sesym->vert); - BLI_assert(se->edge == sesym->edge && se->edge == e); - BLI_assert(sym(se) == sesym && sym(sesym) == se); - for (i = 0; i < 2; i++) { - se = &e->symedges[i]; - v = se->vert; - f = se->face; - BLI_assert(plausible_non_null_ptr(v)); - if (f != NULL) { - BLI_assert(plausible_non_null_ptr(f)); - } - BLI_assert(plausible_non_null_ptr(se->next)); - BLI_assert(plausible_non_null_ptr(se->rot)); - if (check_all_tris && se->face != cdt->outer_face) { - limit = 3; - } - else { - limit = 10000; - } - BLI_assert(reachable(se->next, se, limit)); - if (limit == 3) { - v1 = se->vert; - v2 = se->next->vert; - v3 = se->next->next->vert; - /* The triangle should be positively oriented, but because - * the insertion of intersection vertices doesn't use exact - * arithmetic, this may not be true, so allow a little slop. */ - BLI_assert(orient2d(v1->co, v2->co, v3->co) >= -FLT_EPSILON); - BLI_assert(orient2d(v2->co, v3->co, v1->co) >= -FLT_EPSILON); - BLI_assert(orient2d(v3->co, v1->co, v2->co) >= -FLT_EPSILON); - } - UNUSED_VARS_NDEBUG(limit); - BLI_assert(se->next->next != se); - s = se; - do { - BLI_assert(prev(s)->next == s); - BLI_assert(s->rot == sym(prev(s))); - s = s->next; - } while (s != se); - } - if (check_visibility) { - BLI_assert(isborder || is_visible(se->vert, se->next->vert, false, cdt)); - } - if (!isborder && check_delaunay) { - BLI_assert(is_delaunay_edge(se)); - } - } - totverts = 0; - for (i = 0; i < cdt->vert_array_len; i++) { - v = cdt->vert_array[i]; - BLI_assert(plausible_non_null_ptr(v)); - if (v->merge_to_index != -1) { - BLI_assert(v->merge_to_index >= 0 && v->merge_to_index < cdt->vert_array_len); - continue; - } - totverts++; - BLI_assert(cdt->vert_array_len <= 1 || v->symedge->vert == v); - } - totfaces = 0; - for (ln = cdt->faces; ln; ln = ln->next) { - f = (CDTFace *)ln->link; - BLI_assert(plausible_non_null_ptr(f)); - if (f->deleted) { - continue; - } - totfaces++; - if (f == cdt->outer_face) { - continue; - } - } - /* Euler's formula for planar graphs. */ - if (check_all_tris && totfaces > 1) { - BLI_assert(totverts - totedges + totfaces == 2); - } -} -#endif - -/* Jonathan Shewchuk's adaptive predicates, trimmed to those needed here. - * Permission obtained by private communication from Jonathan to include this code in Blender. - */ - -/* - * Routines for Arbitrary Precision Floating-point Arithmetic - * and Fast Robust Geometric Predicates - * (predicates.c) - * - * May 18, 1996 - * - * Placed in the public domain by - * Jonathan Richard Shewchuk - * School of Computer Science - * Carnegie Mellon University - * 5000 Forbes Avenue - * Pittsburgh, Pennsylvania 15213-3891 - * jrs@cs.cmu.edu - * - * This file contains C implementation of algorithms for exact addition - * and multiplication of floating-point numbers, and predicates for - * robustly performing the orientation and incircle tests used in - * computational geometry. The algorithms and underlying theory are - * described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- - * Point Arithmetic and Fast Robust Geometric Predicates." Technical - * Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon - * University, Pittsburgh, Pennsylvania, May 1996. (Submitted to - * Discrete & Computational Geometry.) - * - * This file, the paper listed above, and other information are available - * from the Web page http://www.cs.cmu.edu/~quake/robust.html . - * - * Using this code: - * - * First, read the short or long version of the paper (from the Web page - * above). - * - * Be sure to call exactinit() once, before calling any of the arithmetic - * functions or geometric predicates. Also be sure to turn on the - * optimizer when compiling this file. - * - * On some machines, the exact arithmetic routines might be defeated by the - * use of internal extended precision floating-point registers. Sometimes - * this problem can be fixed by defining certain values to be volatile, - * thus forcing them to be stored to memory and rounded off. This isn't - * a great solution, though, as it slows the arithmetic down. - * - * To try this out, write "#define INEXACT volatile" below. Normally, - * however, INEXACT should be defined to be nothing. ("#define INEXACT".) - */ - -#define INEXACT /* Nothing */ -/* #define INEXACT volatile */ - -/* Which of the following two methods of finding the absolute values is - * fastest is compiler-dependent. A few compilers can inline and optimize - * the fabs() call; but most will incur the overhead of a function call, - * which is disastrously slow. A faster way on IEEE machines might be to - * mask the appropriate bit, but that's difficult to do in C. - */ - -#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) -/* #define Absolute(a) fabs(a) */ - -/* Many of the operations are broken up into two pieces, a main part that - * performs an approximate operation, and a "tail" that computes the - * roundoff error of that operation. - * - * The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), - * Split(), and Two_Product() are all implemented as described in the - * reference. Each of these macros requires certain variables to be - * defined in the calling routine. The variables `bvirt', `c', `abig', - * `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because - * they store the result of an operation that may incur roundoff error. - * The input parameter `x' (or the highest numbered `x_' parameter) must - * also be declared `INEXACT'. - */ - -#define Fast_Two_Sum_Tail(a, b, x, y) \ - bvirt = x - a; \ - y = b - bvirt - -#define Fast_Two_Sum(a, b, x, y) \ - x = (double)(a + b); \ - Fast_Two_Sum_Tail(a, b, x, y) - -#define Fast_Two_Diff_Tail(a, b, x, y) \ - bvirt = a - x; \ - y = bvirt - b - -#define Fast_Two_Diff(a, b, x, y) \ - x = (double)(a - b); \ - Fast_Two_Diff_Tail(a, b, x, y) - -#define Two_Sum_Tail(a, b, x, y) \ - bvirt = (double)(x - a); \ - avirt = x - bvirt; \ - bround = b - bvirt; \ - around = a - avirt; \ - y = around + bround - -#define Two_Sum(a, b, x, y) \ - x = (double)(a + b); \ - Two_Sum_Tail(a, b, x, y) - -#define Two_Diff_Tail(a, b, x, y) \ - bvirt = (double)(a - x); \ - avirt = x + bvirt; \ - bround = bvirt - b; \ - around = a - avirt; \ - y = around + bround - -#define Two_Diff(a, b, x, y) \ - x = (double)(a - b); \ - Two_Diff_Tail(a, b, x, y) - -#define Split(a, ahi, alo) \ - c = (double)(splitter * a); \ - abig = (double)(c - a); \ - ahi = c - abig; \ - alo = a - ahi - -#define Two_Product_Tail(a, b, x, y) \ - Split(a, ahi, alo); \ - Split(b, bhi, blo); \ - err1 = x - (ahi * bhi); \ - err2 = err1 - (alo * bhi); \ - err3 = err2 - (ahi * blo); \ - y = (alo * blo) - err3 - -#define Two_Product(a, b, x, y) \ - x = (double)(a * b); \ - Two_Product_Tail(a, b, x, y) - -#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ - x = (double)(a * b); \ - Split(a, ahi, alo); \ - err1 = x - (ahi * bhi); \ - err2 = err1 - (alo * bhi); \ - err3 = err2 - (ahi * blo); \ - y = (alo * blo) - err3 - -#define Square_Tail(a, x, y) \ - Split(a, ahi, alo); \ - err1 = x - (ahi * ahi); \ - err3 = err1 - ((ahi + ahi) * alo); \ - y = (alo * alo) - err3 - -#define Square(a, x, y) \ - x = (double)(a * a); \ - Square_Tail(a, x, y) - -#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ - Two_Sum(a0, b, _i, x0); \ - Two_Sum(a1, _i, x2, x1) - -#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ - Two_Diff(a0, b, _i, x0); \ - Two_Sum(a1, _i, x2, x1) - -#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ - Two_One_Sum(a1, a0, b0, _j, _0, x0); \ - Two_One_Sum(_j, _0, b1, x3, x2, x1) - -#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ - Two_One_Diff(a1, a0, b0, _j, _0, x0); \ - Two_One_Diff(_j, _0, b1, x3, x2, x1) - -static double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ -static double m_epsilon; /* = 2^(-p). Used to estimate roundoff errors. */ -/* A set of coefficients used to calculate maximum roundoff errors. */ -static double resulterrbound; -static double ccwerrboundA, ccwerrboundB, ccwerrboundC; -static double o3derrboundA, o3derrboundB, o3derrboundC; -static double iccerrboundA, iccerrboundB, iccerrboundC; -static double isperrboundA, isperrboundB, isperrboundC; - -/* exactinit() Initialize the variables used for exact arithmetic. - * - * `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in - * floating-point arithmetic. `epsilon' bounds the relative roundoff - * error. It is used for floating-point error analysis. - * - * `splitter' is used to split floating-point numbers into two - * half-length significands for exact multiplication. - * - * I imagine that a highly optimizing compiler might be too smart for its - * own good, and somehow cause this routine to fail, if it pretends that - * floating-point arithmetic is too much like real arithmetic. - * - * Don't change this routine unless you fully understand it. - */ - -static void exactinit(void) -{ - double half; - double check, lastcheck; - int every_other; - - every_other = 1; - half = 0.5; - m_epsilon = 1.0; - splitter = 1.0; - check = 1.0; - /* Repeatedly divide `epsilon' by two until it is too small to add to - * one without causing roundoff. (Also check if the sum is equal to - * the previous sum, for machines that round up instead of using exact - * rounding. Not that this library will work on such machines anyway. - */ - do { - lastcheck = check; - m_epsilon *= half; - if (every_other) { - splitter *= 2.0; - } - every_other = !every_other; - check = 1.0 + m_epsilon; - } while ((check != 1.0) && (check != lastcheck)); - splitter += 1.0; - - /* Error bounds for orientation and incircle tests. */ - resulterrbound = (3.0 + 8.0 * m_epsilon) * m_epsilon; - ccwerrboundA = (3.0 + 16.0 * m_epsilon) * m_epsilon; - ccwerrboundB = (2.0 + 12.0 * m_epsilon) * m_epsilon; - ccwerrboundC = (9.0 + 64.0 * m_epsilon) * m_epsilon * m_epsilon; - o3derrboundA = (7.0 + 56.0 * m_epsilon) * m_epsilon; - o3derrboundB = (3.0 + 28.0 * m_epsilon) * m_epsilon; - o3derrboundC = (26.0 + 288.0 * m_epsilon) * m_epsilon * m_epsilon; - iccerrboundA = (10.0 + 96.0 * m_epsilon) * m_epsilon; - iccerrboundB = (4.0 + 48.0 * m_epsilon) * m_epsilon; - iccerrboundC = (44.0 + 576.0 * m_epsilon) * m_epsilon * m_epsilon; - isperrboundA = (16.0 + 224.0 * m_epsilon) * m_epsilon; - isperrboundB = (5.0 + 72.0 * m_epsilon) * m_epsilon; - isperrboundC = (71.0 + 1408.0 * m_epsilon) * m_epsilon * m_epsilon; -} - -/* fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero - * components from the output expansion. - * - * Sets h = e + f. See the long version of my paper for details. - * - * If round-to-even is used (as with IEEE 754), maintains the strongly - * non-overlapping property. (That is, if e is strongly non-overlapping, h - * will be also.) Does NOT maintain the non-overlapping or non-adjacent - * properties. - */ - -static int fast_expansion_sum_zeroelim( - int elen, const double *e, int flen, const double *f, double *h) /* h cannot be e or f. */ -{ - double Q; - INEXACT double Qnew; - INEXACT double hh; - INEXACT double bvirt; - double avirt, bround, around; - int eindex, findex, hindex; - double enow, fnow; - - enow = e[0]; - fnow = f[0]; - eindex = findex = 0; - if ((fnow > enow) == (fnow > -enow)) { - Q = enow; - enow = e[++eindex]; - } - else { - Q = fnow; - fnow = f[++findex]; - } - hindex = 0; - if ((eindex < elen) && (findex < flen)) { - if ((fnow > enow) == (fnow > -enow)) { - Fast_Two_Sum(enow, Q, Qnew, hh); - enow = e[++eindex]; - } - else { - Fast_Two_Sum(fnow, Q, Qnew, hh); - fnow = f[++findex]; - } - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - while ((eindex < elen) && (findex < flen)) { - if ((fnow > enow) == (fnow > -enow)) { - Two_Sum(Q, enow, Qnew, hh); - enow = e[++eindex]; - } - else { - Two_Sum(Q, fnow, Qnew, hh); - fnow = f[++findex]; - } - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - } - while (eindex < elen) { - Two_Sum(Q, enow, Qnew, hh); - enow = e[++eindex]; - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - while (findex < flen) { - Two_Sum(Q, fnow, Qnew, hh); - fnow = f[++findex]; - Q = Qnew; - if (hh != 0.0) { - h[hindex++] = hh; - } - } - if ((Q != 0.0) || (hindex == 0)) { - h[hindex++] = Q; - } - return hindex; -} - -/* scale_expansion_zeroelim() Multiply an expansion by a scalar, - * eliminating zero components from the - * output expansion. - * - * Sets h = be. See either version of my paper for details. - * - * Maintains the nonoverlapping property. If round-to-even is used (as - * with IEEE 754), maintains the strongly nonoverlapping and nonadjacent - * properties as well. (That is, if e has one of these properties, so - * will h.) - */ - -static int scale_expansion_zeroelim(int elen, - const double *e, - double b, - double *h) /* e and h cannot be the same. */ -{ - INEXACT double Q, sum; - double hh; - INEXACT double product1; - double product0; - int eindex, hindex; - double enow; - INEXACT double bvirt; - double avirt, bround, around; - INEXACT double c; - INEXACT double abig; - double ahi, alo, bhi, blo; - double err1, err2, err3; - - Split(b, bhi, blo); - Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); - hindex = 0; - if (hh != 0) { - h[hindex++] = hh; - } - for (eindex = 1; eindex < elen; eindex++) { - enow = e[eindex]; - Two_Product_Presplit(enow, b, bhi, blo, product1, product0); - Two_Sum(Q, product0, sum, hh); - if (hh != 0) { - h[hindex++] = hh; - } - Fast_Two_Sum(product1, sum, Q, hh); - if (hh != 0) { - h[hindex++] = hh; - } - } - if ((Q != 0.0) || (hindex == 0)) { - h[hindex++] = Q; - } - return hindex; -} - -/* estimate() Produce a one-word estimate of an expansion's value. - * - * See either version of my paper for details. - */ - -static double estimate(int elen, const double *e) -{ - double Q; - int eindex; - - Q = e[0]; - for (eindex = 1; eindex < elen; eindex++) { - Q += e[eindex]; - } - return Q; -} - -/* orient2d() Adaptive exact 2D orientation test. Robust. - * - * Return a positive value if the points pa, pb, and pc occur - * in counterclockwise order; a negative value if they occur - * in clockwise order; and zero if they are collinear. The - * result is also a rough approximation of twice the signed - * area of the triangle defined by the three points. - * - * This uses exact arithmetic to ensure a correct answer. The - * result returned is the determinant of a matrix. - * This determinant is computed adaptively, in the sense that exact - * arithmetic is used only to the degree it is needed to ensure that the - * returned value has the correct sign. Hence, orient2d() is usually quite - * fast, but will run more slowly when the input points are collinear or - * nearly so. - */ - -static double orient2dadapt(const double *pa, const double *pb, const double *pc, double detsum) -{ - INEXACT double acx, acy, bcx, bcy; - double acxtail, acytail, bcxtail, bcytail; - INEXACT double detleft, detright; - double detlefttail, detrighttail; - double det, errbound; - double B[4], C1[8], C2[12], D[16]; - INEXACT double B3; - int C1length, C2length, Dlength; - double u[4]; - INEXACT double u3; - INEXACT double s1, t1; - double s0, t0; - - INEXACT double bvirt; - double avirt, bround, around; - INEXACT double c; - INEXACT double abig; - double ahi, alo, bhi, blo; - double err1, err2, err3; - INEXACT double _i, _j; - double _0; - - acx = (double)(pa[0] - pc[0]); - bcx = (double)(pb[0] - pc[0]); - acy = (double)(pa[1] - pc[1]); - bcy = (double)(pb[1] - pc[1]); - - Two_Product(acx, bcy, detleft, detlefttail); - Two_Product(acy, bcx, detright, detrighttail); - - Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]); - B[3] = B3; - - det = estimate(4, B); - errbound = ccwerrboundB * detsum; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pc[0], acx, acxtail); - Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); - Two_Diff_Tail(pa[1], pc[1], acy, acytail); - Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); - - if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) { - return det; - } - - errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); - det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Product(acxtail, bcy, s1, s0); - Two_Product(acytail, bcx, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); - - Two_Product(acx, bcytail, s1, s0); - Two_Product(acy, bcxtail, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); - - Two_Product(acxtail, bcytail, s1, s0); - Two_Product(acytail, bcxtail, t1, t0); - Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); - u[3] = u3; - Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); - - return (D[Dlength - 1]); -} - -static double orient2d(const double *pa, const double *pb, const double *pc) -{ - double detleft, detright, det; - double detsum, errbound; - - detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); - detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); - det = detleft - detright; - - if (detleft > 0.0) { - if (detright <= 0.0) { - return det; - } - detsum = detleft + detright; - } - else if (detleft < 0.0) { - if (detright >= 0.0) { - return det; - } - detsum = -detleft - detright; - } - else { - return det; - } - - errbound = ccwerrboundA * detsum; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - return orient2dadapt(pa, pb, pc, detsum); -} - -/* incircle() Adaptive exact 2D incircle test. Robust. - * - * Return a positive value if the point pd lies inside the - * circle passing through pa, pb, and pc; a negative value if - * it lies outside; and zero if the four points are cocircular. - * The points pa, pb, and pc must be in counterclockwise - * order, or the sign of the result will be reversed. - * - * This uses exact arithmetic to ensure a correct answer. - * The result returned is the determinant of a matrix. - * This determinant is computed adaptively, in the sense that exact - * arithmetic is used only to the degree it is needed to ensure that the - * returned value has the correct sign. Hence, incircle() is usually quite - * fast, but will run more slowly when the input points are cocircular or - * nearly so. - * - * This function is allowed to be long for two reasons. Firstly, it was taken - * from an external source and only slightly adapted, and keeping its original - * form will make integration of upstream changes easier. Secondly, it is very - * sensitive to floating point errors, and refactoring may break it in subtle - * and hard to detect ways. - * NOLINTNEXTLINE: readability-function-size */ -static double incircleadapt( - const double *pa, const double *pb, const double *pc, const double *pd, double permanent) -{ - INEXACT double adx, bdx, cdx, ady, bdy, cdy; - double det, errbound; - - INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; - double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; - double bc[4], ca[4], ab[4]; - INEXACT double bc3, ca3, ab3; - double axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; - int axbclen, axxbclen, aybclen, ayybclen, alen; - double bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; - int bxcalen, bxxcalen, bycalen, byycalen, blen; - double cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; - int cxablen, cxxablen, cyablen, cyyablen, clen; - double abdet[64]; - int ablen; - double fin1[1152], fin2[1152]; - double *finnow, *finother, *finswap; - int finlength; - - double adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; - INEXACT double adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; - double adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; - double aa[4], bb[4], cc[4]; - INEXACT double aa3, bb3, cc3; - INEXACT double ti1, tj1; - double ti0, tj0; - double u[4], v[4]; - INEXACT double u3, v3; - double temp8[8], temp16a[16], temp16b[16], temp16c[16]; - double temp32a[32], temp32b[32], temp48[48], temp64[64]; - int temp8len, temp16alen, temp16blen, temp16clen; - int temp32alen, temp32blen, temp48len, temp64len; - double axtbb[8], axtcc[8], aytbb[8], aytcc[8]; - int axtbblen, axtcclen, aytbblen, aytcclen; - double bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; - int bxtaalen, bxtcclen, bytaalen, bytcclen; - double cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; - int cxtaalen, cxtbblen, cytaalen, cytbblen; - double axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; - int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; - double axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; - int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; - double axtbctt[8], aytbctt[8], bxtcatt[8]; - double bytcatt[8], cxtabtt[8], cytabtt[8]; - int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; - double abt[8], bct[8], cat[8]; - int abtlen, bctlen, catlen; - double abtt[4], bctt[4], catt[4]; - int abttlen, bcttlen, cattlen; - INEXACT double abtt3, bctt3, catt3; - double negate; - - INEXACT double bvirt; - double avirt, bround, around; - INEXACT double c; - INEXACT double abig; - double ahi, alo, bhi, blo; - double err1, err2, err3; - INEXACT double _i, _j; - double _0; - - adx = (double)(pa[0] - pd[0]); - bdx = (double)(pb[0] - pd[0]); - cdx = (double)(pc[0] - pd[0]); - ady = (double)(pa[1] - pd[1]); - bdy = (double)(pb[1] - pd[1]); - cdy = (double)(pc[1] - pd[1]); - - Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); - Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); - Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); - bc[3] = bc3; - axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); - axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); - aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); - ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); - alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); - - Two_Product(cdx, ady, cdxady1, cdxady0); - Two_Product(adx, cdy, adxcdy1, adxcdy0); - Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); - ca[3] = ca3; - bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); - bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); - bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); - byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); - blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); - - Two_Product(adx, bdy, adxbdy1, adxbdy0); - Two_Product(bdx, ady, bdxady1, bdxady0); - Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); - ab[3] = ab3; - cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); - cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); - cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); - cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); - clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); - - ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); - finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); - - det = estimate(finlength, fin1); - errbound = iccerrboundB * permanent; - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - Two_Diff_Tail(pa[0], pd[0], adx, adxtail); - Two_Diff_Tail(pa[1], pd[1], ady, adytail); - Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); - Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); - Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); - Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); - if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) && - (bdytail == 0.0) && (cdytail == 0.0)) { - return det; - } - - errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); - det += ((adx * adx + ady * ady) * - ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + - 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + - ((bdx * bdx + bdy * bdy) * - ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + - 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + - ((cdx * cdx + cdy * cdy) * - ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + - 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); - if ((det >= errbound) || (-det >= errbound)) { - return det; - } - - finnow = fin1; - finother = fin2; - - if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { - Square(adx, adxadx1, adxadx0); - Square(ady, adyady1, adyady0); - Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); - aa[3] = aa3; - } - if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { - Square(bdx, bdxbdx1, bdxbdx0); - Square(bdy, bdybdy1, bdybdy0); - Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); - bb[3] = bb3; - } - if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { - Square(cdx, cdxcdx1, cdxcdx0); - Square(cdy, cdycdy1, cdycdy0); - Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); - cc[3] = cc3; - } - - if (adxtail != 0.0) { - axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); - temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a); - - axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); - temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); - - axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); - temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (adytail != 0.0) { - aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); - temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a); - - aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); - temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); - - aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); - temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (bdxtail != 0.0) { - bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); - temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a); - - bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); - temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); - - bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); - temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (bdytail != 0.0) { - bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); - temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a); - - bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); - temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); - - bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); - temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (cdxtail != 0.0) { - cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); - temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a); - - cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); - temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); - - cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); - temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (cdytail != 0.0) { - cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); - temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a); - - cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); - temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); - - cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); - temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); - - temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - - if ((adxtail != 0.0) || (adytail != 0.0)) { - if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { - Two_Product(bdxtail, cdy, ti1, ti0); - Two_Product(bdx, cdytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -bdy; - Two_Product(cdxtail, negate, ti1, ti0); - negate = -bdytail; - Two_Product(cdx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); - - Two_Product(bdxtail, cdytail, ti1, ti0); - Two_Product(cdxtail, bdytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); - bctt[3] = bctt3; - bcttlen = 4; - } - else { - bct[0] = 0.0; - bctlen = 1; - bctt[0] = 0.0; - bcttlen = 1; - } - - if (adxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); - axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); - temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - if (bdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (cdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a); - axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); - temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a); - temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (adytail != 0.0) { - temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); - aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); - temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - - temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a); - aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); - temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a); - temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - } - if ((bdxtail != 0.0) || (bdytail != 0.0)) { - if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { - Two_Product(cdxtail, ady, ti1, ti0); - Two_Product(cdx, adytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -cdy; - Two_Product(adxtail, negate, ti1, ti0); - negate = -cdytail; - Two_Product(adx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); - - Two_Product(cdxtail, adytail, ti1, ti0); - Two_Product(adxtail, cdytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); - catt[3] = catt3; - cattlen = 4; - } - else { - cat[0] = 0.0; - catlen = 1; - catt[0] = 0.0; - cattlen = 1; - } - - if (bdxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); - bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); - temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - if (cdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (adytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a); - bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); - temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a); - temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (bdytail != 0.0) { - temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); - bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); - temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - - temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a); - bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); - temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a); - temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - } - if ((cdxtail != 0.0) || (cdytail != 0.0)) { - if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { - Two_Product(adxtail, bdy, ti1, ti0); - Two_Product(adx, bdytail, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); - u[3] = u3; - negate = -ady; - Two_Product(bdxtail, negate, ti1, ti0); - negate = -adytail; - Two_Product(bdx, negate, tj1, tj0); - Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); - v[3] = v3; - abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); - - Two_Product(adxtail, bdytail, ti1, ti0); - Two_Product(bdxtail, adytail, tj1, tj0); - Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); - abtt[3] = abtt3; - abttlen = 4; - } - else { - abt[0] = 0.0; - abtlen = 1; - abtt[0] = 0.0; - abttlen = 1; - } - - if (cdxtail != 0.0) { - temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); - cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); - temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - if (adytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (bdytail != 0.0) { - temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); - temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - - temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a); - cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); - temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a); - temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - if (cdytail != 0.0) { - temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); - cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); - temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a); - temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - - temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a); - cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); - temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a); - temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b); - temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); - temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); - finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); - finswap = finnow; - finnow = finother; - finother = finswap; - } - } - - return finnow[finlength - 1]; -} - -static double incircle(const double *pa, const double *pb, const double *pc, const double *pd) -{ - double adx, bdx, cdx, ady, bdy, cdy; - double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; - double alift, blift, clift; - double det; - double permanent, errbound; - - adx = pa[0] - pd[0]; - bdx = pb[0] - pd[0]; - cdx = pc[0] - pd[0]; - ady = pa[1] - pd[1]; - bdy = pb[1] - pd[1]; - cdy = pc[1] - pd[1]; - - bdxcdy = bdx * cdy; - cdxbdy = cdx * bdy; - alift = adx * adx + ady * ady; - - cdxady = cdx * ady; - adxcdy = adx * cdy; - blift = bdx * bdx + bdy * bdy; - - adxbdy = adx * bdy; - bdxady = bdx * ady; - clift = cdx * cdx + cdy * cdy; - - det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady); - - permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + - (Absolute(cdxady) + Absolute(adxcdy)) * blift + - (Absolute(adxbdy) + Absolute(bdxady)) * clift; - errbound = iccerrboundA * permanent; - if ((det > errbound) || (-det > errbound)) { - return det; - } - - return incircleadapt(pa, pb, pc, pd, permanent); -} diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc new file mode 100644 index 00000000000..7b0f6a658ce --- /dev/null +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -0,0 +1,2500 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + */ + +#include <algorithm> +#include <fstream> +#include <iostream> +#include <sstream> + +#include "BLI_array.hh" +#include "BLI_double2.hh" +#include "BLI_linklist.h" +#include "BLI_math_boolean.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mpq2.hh" +#include "BLI_vector.hh" + +#include "BLI_delaunay_2d.h" + +namespace blender::meshintersect { + +/* Throughout this file, template argument T will be an + * arithmetic-like type, like float, double, or mpq_class. */ + +template<typename T> T math_abs(const T v) +{ + return (v < 0) ? -v : v; +} + +#ifdef WITH_GMP +template<> mpq_class math_abs<mpq_class>(const mpq_class v) +{ + return abs(v); +} +#endif + +template<> double math_abs<double>(const double v) +{ + return fabs(v); +} + +template<typename T> double math_to_double(const T UNUSED(v)) +{ + BLI_assert(false); /* Need implementation for other type. */ + return 0.0; +} + +#ifdef WITH_GMP +template<> double math_to_double<mpq_class>(const mpq_class v) +{ + return v.get_d(); +} +#endif + +template<> double math_to_double<double>(const double v) +{ + return v; +} + +/** + * Define a templated 2D arrangement of vertices, edges, and faces. + * The #SymEdge data structure is the basis for a structure that allows + * easy traversal to neighboring (by topology) geometric elements. + * Each of #CDTVert, #CDTEdge, and #CDTFace have an input_id linked list, + * whose nodes contain integers that keep track of which input verts, edges, + * and faces, respectively, that the element was derived from. + * + * While this could be cleaned up some, it is usable by other routines in Blender + * that need to keep track of a 2D arrangement, with topology. + */ +template<typename Arith_t> struct CDTVert; +template<typename Arith_t> struct CDTEdge; +template<typename Arith_t> struct CDTFace; + +template<typename Arith_t> struct SymEdge { + /** Next #SymEdge in face, doing CCW traversal of face. */ + SymEdge<Arith_t> *next{nullptr}; + /** Next #SymEdge CCW around vert. */ + SymEdge<Arith_t> *rot{nullptr}; + /** Vert at origin. */ + CDTVert<Arith_t> *vert{nullptr}; + /** Un-directed edge this is for. */ + CDTEdge<Arith_t> *edge{nullptr}; + /** Face on left side. */ + CDTFace<Arith_t> *face{nullptr}; + + SymEdge() = default; +}; + +/** + * Return other #SymEdge for same #CDTEdge as \a se. + */ +template<typename T> inline SymEdge<T> *sym(const SymEdge<T> *se) +{ + return se->next->rot; +} + +/** Return #SymEdge whose next is \a se. */ +template<typename T> inline SymEdge<T> *prev(const SymEdge<T> *se) +{ + return se->rot->next->rot; +} + +template<typename Arith_t> struct CDTVert { + /** Coordinate. */ + vec2<Arith_t> co; + /** Some edge attached to it. */ + SymEdge<Arith_t> *symedge{nullptr}; + /** List of corresponding vertex input ids. */ + LinkNode *input_ids{nullptr}; + /** Index into array that #CDTArrangement keeps. */ + int index{-1}; + /** Index of a CDTVert that this has merged to. -1 if no merge. */ + int merge_to_index{-1}; + /** Used by algorithms operating on CDT structures. */ + int visit_index{0}; + + CDTVert() = default; + explicit CDTVert(const vec2<Arith_t> &pt); +}; + +template<typename Arith_t> struct CDTEdge { + /** List of input edge ids that this is part of. */ + LinkNode *input_ids{nullptr}; + /** The directed edges for this edge. */ + SymEdge<Arith_t> symedges[2]{SymEdge<Arith_t>(), SymEdge<Arith_t>()}; + + CDTEdge() = default; +}; + +template<typename Arith_t> struct CDTFace { + /** A symedge in face; only used during output, so only valid then. */ + SymEdge<Arith_t> *symedge{nullptr}; + /** List of input face ids that this is part of. */ + LinkNode *input_ids{nullptr}; + /** Used by algorithms operating on CDT structures. */ + int visit_index{0}; + /** Marks this face no longer used. */ + bool deleted{false}; + + CDTFace() = default; +}; + +template<typename Arith_t> struct CDTArrangement { + /* The arrangement owns the memory pointed to by the pointers in these vectors. + * They are pointers instead of actual structures because these vectors may be resized and + * other elements refer to the elements by pointer. */ + + /** The verts. Some may be merged to others (see their merge_to_index). */ + Vector<CDTVert<Arith_t> *> verts; + /** The edges. Some may be deleted (SymEdge next and rot pointers are null). */ + Vector<CDTEdge<Arith_t> *> edges; + /** The faces. Some may be deleted (see their delete member). */ + Vector<CDTFace<Arith_t> *> faces; + /** Which CDTFace is the outer face. */ + CDTFace<Arith_t> *outer_face{nullptr}; + + CDTArrangement() = default; + ~CDTArrangement(); + + /** Hint to how much space to reserve in the Vectors of the arrangement, + * based on these counts of input elements. */ + void reserve(int num_verts, int num_edges, int num_faces); + + /** + * Add a new vertex to the arrangement, with the given 2D coordinate. + * It will not be connected to anything yet. + */ + CDTVert<Arith_t> *add_vert(const vec2<Arith_t> &pt); + + /** + * Add an edge from v1 to v2. The edge will have a left face and a right face, + * specified by \a fleft and \a fright. The edge will not be connected to anything yet. + * If the vertices do not yet have a #SymEdge pointer, + * their pointer is set to the #SymEdge in this new edge. + */ + CDTEdge<Arith_t> *add_edge(CDTVert<Arith_t> *v1, + CDTVert<Arith_t> *v2, + CDTFace<Arith_t> *fleft, + CDTFace<Arith_t> *fright); + + /** + * Add a new face. It is disconnected until an add_edge makes it the + * left or right face of an edge. + */ + CDTFace<Arith_t> *add_face(); + + /** Make a new edge from v to se->vert, splicing it in. */ + CDTEdge<Arith_t> *add_vert_to_symedge_edge(CDTVert<Arith_t> *v, SymEdge<Arith_t> *se); + + /** + * Assuming s1 and s2 are both #SymEdge's in a face with > 3 sides and one is not the next of the + * other, Add an edge from `s1->v` to `s2->v`, splitting the face in two. The original face will + * be the one that s1 has as left face, and a new face will be added and made s2 and its + * next-cycle's left face. + */ + CDTEdge<Arith_t> *add_diagonal(SymEdge<Arith_t> *s1, SymEdge<Arith_t> *s2); + + /** + * Connect the verts of se1 and se2, assuming that currently those two #SymEdge's are on the + * outer boundary (have face == outer_face) of two components that are isolated from each other. + */ + CDTEdge<Arith_t> *connect_separate_parts(SymEdge<Arith_t> *se1, SymEdge<Arith_t> *se2); + + /** + * Split se at fraction lambda, and return the new #CDTEdge that is the new second half. + * Copy the edge input_ids into the new one. + */ + CDTEdge<Arith_t> *split_edge(SymEdge<Arith_t> *se, Arith_t lambda); + + /** + * Delete an edge. The new combined face on either side of the deleted edge will be the one that + * was e's face. There will now be an unused face, which will be marked deleted, and an unused + * #CDTEdge, marked by setting the next and rot pointers of its #SymEdge's to #nullptr. + */ + void delete_edge(SymEdge<Arith_t> *se); + + /** + * If the vertex with index i in the vert array has not been merge, return it. + * Else return the one that it has merged to. + */ + CDTVert<Arith_t> *get_vert_resolve_merge(int i) + { + CDTVert<Arith_t> *v = this->verts[i]; + if (v->merge_to_index != -1) { + v = this->verts[v->merge_to_index]; + } + return v; + } +}; + +template<typename T> class CDT_state { + public: + CDTArrangement<T> cdt; + /** How many verts were in input (will be first in vert_array). */ + int input_vert_tot; + /** Used for visiting things without having to initialized their visit fields. */ + int visit_count; + /** + * Edge ids for face start with this, and each face gets this much index space + * to encode positions within the face. + */ + int face_edge_offset; + /** How close before coords considered equal. */ + T epsilon; + + explicit CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon); + ~CDT_state() + { + } +}; + +template<typename T> CDTArrangement<T>::~CDTArrangement() +{ + for (int i : this->verts.index_range()) { + CDTVert<T> *v = this->verts[i]; + BLI_linklist_free(v->input_ids, nullptr); + delete v; + this->verts[i] = nullptr; + } + for (int i : this->edges.index_range()) { + CDTEdge<T> *e = this->edges[i]; + BLI_linklist_free(e->input_ids, nullptr); + delete e; + this->edges[i] = nullptr; + } + for (int i : this->faces.index_range()) { + CDTFace<T> *f = this->faces[i]; + BLI_linklist_free(f->input_ids, nullptr); + delete f; + this->faces[i] = nullptr; + } +} + +#define DEBUG_CDT +#ifdef DEBUG_CDT +/* Some functions to aid in debugging. */ +template<typename T> std::string vertname(const CDTVert<T> *v) +{ + std::stringstream ss; + ss << "[" << v->index << "]"; + return ss.str(); +} + +/* Abbreviated pointer value is easier to read in dumps. */ +static std::string trunc_ptr(const void *p) +{ + constexpr int TRUNC_PTR_MASK = 0xFFFF; + std::stringstream ss; + ss << std::hex << (POINTER_AS_INT(p) & TRUNC_PTR_MASK); + return ss.str(); +} + +template<typename T> std::string sename(const SymEdge<T> *se) +{ + std::stringstream ss; + ss << "{" << trunc_ptr(se) << "}"; + return ss.str(); +} + +template<typename T> std::ostream &operator<<(std::ostream &os, const SymEdge<T> &se) +{ + if (se.next) { + os << vertname(se.vert) << "(" << se.vert->co << "->" << se.next->vert->co << ")" + << vertname(se.next->vert); + } + else { + os << vertname(se.vert) << "(" << se.vert->co << "->NULL)"; + } + return os; +} + +template<typename T> std::ostream &operator<<(std::ostream &os, const SymEdge<T> *se) +{ + os << *se; + return os; +} + +template<typename T> std::string short_se_dump(const SymEdge<T> *se) +{ + if (se == nullptr) { + return std::string("NULL"); + } + return vertname(se->vert) + + (se->next == nullptr ? std::string("[NULL]") : vertname(se->next->vert)); +} + +template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_state<T> &cdt_state) +{ + os << "\nCDT\n\nVERTS\n"; + for (const CDTVert<T> *v : cdt_state.cdt.verts) { + os << vertname(v) << " " << trunc_ptr(v) << ": " << v->co + << " symedge=" << trunc_ptr(v->symedge); + if (v->merge_to_index == -1) { + os << "\n"; + } + else { + os << " merge to " << vertname(cdt_state.cdt.verts[v->merge_to_index]) << "\n"; + } + const SymEdge<T> *se = v->symedge; + int cnt = 0; + constexpr int print_count_limit = 25; + if (se) { + os << " edges out:\n"; + do { + if (se->next == NULL) { + os << " [NULL] next/rot symedge, se=" << trunc_ptr(se) << "\n"; + break; + } + if (se->next->next == NULL) { + os << " [NULL] next-next/rot symedge, se=" << trunc_ptr(se) << "\n"; + break; + } + const CDTVert<T> *vother = sym(se)->vert; + os << " " << vertname(vother) << "(e=" << trunc_ptr(se->edge) + << ", se=" << trunc_ptr(se) << ")\n"; + se = se->rot; + cnt++; + } while (se != v->symedge && cnt < print_count_limit); + os << "\n"; + } + } + os << "\nEDGES\n"; + for (const CDTEdge<T> *e : cdt_state.cdt.edges) { + if (e->symedges[0].next == nullptr) { + continue; + } + os << trunc_ptr(&e) << ":\n"; + for (int i = 0; i < 2; ++i) { + const SymEdge<T> *se = &e->symedges[i]; + os << " se[" << i << "] @" << trunc_ptr(se) << " next=" << trunc_ptr(se->next) + << ", rot=" << trunc_ptr(se->rot) << ", vert=" << trunc_ptr(se->vert) << " " + << vertname(se->vert) << " " << se->vert->co << ", edge=" << trunc_ptr(se->edge) + << ", face=" << trunc_ptr(se->face) << "\n"; + } + } + os << "\nFACES\n"; + os << "outer_face=" << trunc_ptr(cdt_state.cdt.outer_face) << "\n"; + /* Only after prepare_output do faces have non-null symedges. */ + if (cdt_state.cdt.outer_face->symedge != nullptr) { + for (const CDTFace<T> *f : cdt_state.cdt.faces) { + if (!f->deleted) { + os << trunc_ptr(f) << " symedge=" << trunc_ptr(f->symedge) << "\n"; + } + } + } + return os; +} + +template<typename T> void cdt_draw(const std::string &label, const CDTArrangement<T> &cdt) +{ + static bool append = false; /* Will be set to true after first call. */ + +/* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library. + * This is just for developer debugging anyway, and should never be called in production Blender. + */ +# ifdef _WIN32 + const char *drawfile = "./debug_draw.html"; +# else + const char *drawfile = "/tmp/debug_draw.html"; +# endif + constexpr int max_draw_width = 1800; + constexpr int max_draw_height = 1600; + constexpr double margin_expand = 0.05; + constexpr int thin_line = 1; + constexpr int thick_line = 4; + constexpr int vert_radius = 3; + constexpr bool draw_vert_labels = true; + constexpr bool draw_edge_labels = false; + + if (cdt.verts.size() == 0) { + return; + } + vec2<double> vmin(DBL_MAX, DBL_MAX); + vec2<double> vmax(-DBL_MAX, -DBL_MAX); + for (const CDTVert<T> *v : cdt.verts) { + for (int i = 0; i < 2; ++i) { + double dvi = math_to_double(v->co[i]); + if (dvi < vmin[i]) { + vmin[i] = dvi; + } + if (dvi > vmax[i]) { + vmax[i] = dvi; + } + } + } + double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * margin_expand; + double minx = vmin.x - draw_margin; + double maxx = vmax.x + draw_margin; + double miny = vmin.y - draw_margin; + double maxy = vmax.y + draw_margin; + + double width = maxx - minx; + double height = maxy - miny; + double aspect = height / width; + int view_width = max_draw_width; + int view_height = static_cast<int>(view_width * aspect); + if (view_height > max_draw_height) { + view_height = max_draw_height; + view_width = static_cast<int>(view_height / aspect); + } + double scale = view_width / width; + +# define SX(x) ((math_to_double(x) - minx) * scale) +# define SY(y) ((maxy - math_to_double(y)) * scale) + + std::ofstream f; + if (append) { + f.open(drawfile, std::ios_base::app); + } + else { + f.open(drawfile); + } + if (!f) { + std::cout << "Could not open file " << drawfile << "\n"; + return; + } + + f << "<div>" << label << "</div>\n<div>\n" + << "<svg version=\"1.1\" " + "xmlns=\"http://www.w3.org/2000/svg\" " + "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " + "xml:space=\"preserve\"\n" + << "width=\"" << view_width << "\" height=\"" << view_height << "\">n"; + + for (const CDTEdge<T> *e : cdt.edges) { + if (e->symedges[0].next == nullptr) { + continue; + } + const CDTVert<T> *u = e->symedges[0].vert; + const CDTVert<T> *v = e->symedges[1].vert; + const vec2<T> &uco = u->co; + const vec2<T> &vco = v->co; + int strokew = e->input_ids == nullptr ? thin_line : thick_line; + f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\"" + << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\"" + << SY(vco[1]) << "\">\n"; + f << " <title>" << vertname(u) << vertname(v) << "</title>\n"; + f << "</line>\n"; + if (draw_edge_labels) { + f << "<text x=\"" << SX((uco[0] + vco[0]) / 2) << "\" y=\"" << SY((uco[1] + vco[1]) / 2) + << "\" font-size=\"small\">"; + f << vertname(u) << vertname(v) << sename(&e->symedges[0]) << sename(&e->symedges[1]) + << "</text>\n"; + } + } + + int i = 0; + for (const CDTVert<T> *v : cdt.verts) { + f << "<circle fill=\"black\" cx=\"" << SX(v->co[0]) << "\" cy=\"" << SY(v->co[1]) << "\" r=\"" + << vert_radius << "\">\n"; + f << " <title>[" << i << "]" << v->co << "</title>\n"; + f << "</circle>\n"; + if (draw_vert_labels) { + f << "<text x=\"" << SX(v->co[0]) + vert_radius << "\" y=\"" << SY(v->co[1]) - vert_radius + << "\" font-size=\"small\">[" << i << "]</text>\n"; + } + ++i; + } + + append = true; +# undef SX +# undef SY +} +#endif + +/** + * Return true if `a -- b -- c` are in that order, assuming they are on a straight line according + * to #orient2d and we know the order is either `abc` or `bac`. + * This means `ab . ac` and `bc . ac` must both be non-negative. + */ +template<typename T> bool in_line(const vec2<T> &a, const vec2<T> &b, const vec2<T> &c) +{ + vec2<T> ab = b - a; + vec2<T> bc = c - b; + vec2<T> ac = c - a; + if (vec2<T>::dot(ab, ac) < 0) { + return false; + } + return vec2<T>::dot(bc, ac) >= 0; +} + +template<typename T> CDTVert<T>::CDTVert(const vec2<T> &pt) +{ + this->co = pt; + this->input_ids = nullptr; + this->symedge = nullptr; + this->index = -1; + this->merge_to_index = -1; + this->visit_index = 0; +} + +template<typename T> CDTVert<T> *CDTArrangement<T>::add_vert(const vec2<T> &pt) +{ + CDTVert<T> *v = new CDTVert<T>(pt); + int index = this->verts.append_and_get_index(v); + v->index = index; + return v; +} + +template<typename T> +CDTEdge<T> *CDTArrangement<T>::add_edge(CDTVert<T> *v1, + CDTVert<T> *v2, + CDTFace<T> *fleft, + CDTFace<T> *fright) +{ + CDTEdge<T> *e = new CDTEdge<T>(); + this->edges.append(e); + SymEdge<T> *se = &e->symedges[0]; + SymEdge<T> *sesym = &e->symedges[1]; + se->edge = sesym->edge = e; + se->face = fleft; + sesym->face = fright; + se->vert = v1; + if (v1->symedge == nullptr) { + v1->symedge = se; + } + sesym->vert = v2; + if (v2->symedge == nullptr) { + v2->symedge = sesym; + } + se->next = sesym->next = se->rot = sesym->rot = nullptr; + return e; +} + +template<typename T> CDTFace<T> *CDTArrangement<T>::add_face() +{ + CDTFace<T> *f = new CDTFace<T>(); + this->faces.append(f); + return f; +} + +template<typename T> void CDTArrangement<T>::reserve(int num_verts, int num_edges, int num_faces) +{ + /* These reserves are just guesses; OK if they aren't exactly right since vectors will resize. */ + this->verts.reserve(2 * num_verts); + this->edges.reserve(3 * num_verts + 2 * num_edges + 3 * 2 * num_faces); + this->faces.reserve(2 * num_verts + 2 * num_edges + 2 * num_faces); +} + +template<typename T> +CDT_state<T>::CDT_state(int num_input_verts, int num_input_edges, int num_input_faces, T epsilon) +{ + this->input_vert_tot = num_input_verts; + this->cdt.reserve(num_input_verts, num_input_edges, num_input_faces); + this->cdt.outer_face = this->cdt.add_face(); + this->epsilon = epsilon; + this->visit_count = 0; +} + +static bool id_in_list(const LinkNode *id_list, int id) +{ + const LinkNode *ln; + + for (ln = id_list; ln != nullptr; ln = ln->next) { + if (POINTER_AS_INT(ln->link) == id) { + return true; + } + } + return false; +} + +/* Is any id in (range_start, range_start+1, ... , range_end) in id_list? */ +static bool id_range_in_list(const LinkNode *id_list, int range_start, int range_end) +{ + const LinkNode *ln; + int id; + + for (ln = id_list; ln != nullptr; ln = ln->next) { + id = POINTER_AS_INT(ln->link); + if (id >= range_start && id <= range_end) { + return true; + } + } + return false; +} + +static void add_to_input_ids(LinkNode **dst, int input_id) +{ + if (!id_in_list(*dst, input_id)) { + BLI_linklist_prepend(dst, POINTER_FROM_INT(input_id)); + } +} + +static void add_list_to_input_ids(LinkNode **dst, const LinkNode *src) +{ + const LinkNode *ln; + + for (ln = src; ln != nullptr; ln = ln->next) { + add_to_input_ids(dst, POINTER_AS_INT(ln->link)); + } +} + +template<typename T> inline bool is_border_edge(const CDTEdge<T> *e, const CDT_state<T> *cdt) +{ + return e->symedges[0].face == cdt->outer_face || e->symedges[1].face == cdt->outer_face; +} + +template<typename T> inline bool is_constrained_edge(const CDTEdge<T> *e) +{ + return e->input_ids != NULL; +} + +template<typename T> inline bool is_deleted_edge(const CDTEdge<T> *e) +{ + return e->symedges[0].next == NULL; +} + +template<typename T> inline bool is_original_vert(const CDTVert<T> *v, CDT_state<T> *cdt) +{ + return (v->index < cdt->input_vert_tot); +} + +/* Return the Symedge that goes from v1 to v2, if it exists, else return nullptr. */ +template<typename T> +SymEdge<T> *find_symedge_between_verts(const CDTVert<T> *v1, const CDTVert<T> *v2) +{ + SymEdge<T> *t = v1->symedge; + SymEdge<T> *tstart = t; + do { + if (t->next->vert == v2) { + return t; + } + } while ((t = t->rot) != tstart); + return nullptr; +} + +/** + * Return the SymEdge attached to v that has face f, if it exists, else return nullptr. + */ +template<typename T> SymEdge<T> *find_symedge_with_face(const CDTVert<T> *v, const CDTFace<T> *f) +{ + SymEdge<T> *t = v->symedge; + SymEdge<T> *tstart = t; + do { + if (t->face == f) { + return t; + } + } while ((t = t->rot) != tstart); + return nullptr; +} + +/** + * Is there already an edge between a and b? + */ +template<typename T> inline bool exists_edge(const CDTVert<T> *v1, const CDTVert<T> *v2) +{ + return find_symedge_between_verts(v1, v2) != nullptr; +} + +/** + * Is the vertex v incident on face f? + */ +template<typename T> bool vert_touches_face(const CDTVert<T> *v, const CDTFace<T> *f) +{ + SymEdge<T> *se = v->symedge; + do { + if (se->face == f) { + return true; + } + } while ((se = se->rot) != v->symedge); + return false; +} + +/** + * Assume s1 and s2 are both #SymEdges in a face with > 3 sides, + * and one is not the next of the other. + * Add an edge from `s1->v` to `s2->v`, splitting the face in two. + * The original face will continue to be associated with the sub-face + * that has s1, and a new face will be made for s2's new face. + * Return the new diagonal's #CDTEdge pointer. + */ +template<typename T> CDTEdge<T> *CDTArrangement<T>::add_diagonal(SymEdge<T> *s1, SymEdge<T> *s2) +{ + CDTFace<T> *fold = s1->face; + CDTFace<T> *fnew = this->add_face(); + SymEdge<T> *s1prev = prev(s1); + SymEdge<T> *s1prevsym = sym(s1prev); + SymEdge<T> *s2prev = prev(s2); + SymEdge<T> *s2prevsym = sym(s2prev); + CDTEdge<T> *ediag = this->add_edge(s1->vert, s2->vert, fnew, fold); + SymEdge<T> *sdiag = &ediag->symedges[0]; + SymEdge<T> *sdiagsym = &ediag->symedges[1]; + sdiag->next = s2; + sdiagsym->next = s1; + s2prev->next = sdiagsym; + s1prev->next = sdiag; + s1->rot = sdiag; + sdiag->rot = s1prevsym; + s2->rot = sdiagsym; + sdiagsym->rot = s2prevsym; + for (SymEdge<T> *se = s2; se != sdiag; se = se->next) { + se->face = fnew; + } + add_list_to_input_ids(&fnew->input_ids, fold->input_ids); + return ediag; +} + +template<typename T> +CDTEdge<T> *CDTArrangement<T>::add_vert_to_symedge_edge(CDTVert<T> *v, SymEdge<T> *se) +{ + SymEdge<T> *se_rot = se->rot; + SymEdge<T> *se_rotsym = sym(se_rot); + /* TODO: check: I think last arg in next should be sym(se)->face. */ + CDTEdge<T> *e = this->add_edge(v, se->vert, se->face, se->face); + SymEdge<T> *new_se = &e->symedges[0]; + SymEdge<T> *new_se_sym = &e->symedges[1]; + new_se->next = se; + new_se_sym->next = new_se; + new_se->rot = new_se; + new_se_sym->rot = se_rot; + se->rot = new_se_sym; + se_rotsym->next = new_se_sym; + return e; +} + +/** + * Connect the verts of se1 and se2, assuming that currently those two #SymEdge's are on + * the outer boundary (have face == outer_face) of two components that are isolated from + * each other. + */ +template<typename T> +CDTEdge<T> *CDTArrangement<T>::connect_separate_parts(SymEdge<T> *se1, SymEdge<T> *se2) +{ + BLI_assert(se1->face == this->outer_face && se2->face == this->outer_face); + SymEdge<T> *se1_rot = se1->rot; + SymEdge<T> *se1_rotsym = sym(se1_rot); + SymEdge<T> *se2_rot = se2->rot; + SymEdge<T> *se2_rotsym = sym(se2_rot); + CDTEdge<T> *e = this->add_edge(se1->vert, se2->vert, this->outer_face, this->outer_face); + SymEdge<T> *new_se = &e->symedges[0]; + SymEdge<T> *new_se_sym = &e->symedges[1]; + new_se->next = se2; + new_se_sym->next = se1; + new_se->rot = se1_rot; + new_se_sym->rot = se2_rot; + se1->rot = new_se; + se2->rot = new_se_sym; + se1_rotsym->next = new_se; + se2_rotsym->next = new_se_sym; + return e; +} + +/** + * Split se at fraction lambda, + * and return the new #CDTEdge that is the new second half. + * Copy the edge input_ids into the new one. + */ +template<typename T> CDTEdge<T> *CDTArrangement<T>::split_edge(SymEdge<T> *se, T lambda) +{ + /* Split e at lambda. */ + const vec2<T> *a = &se->vert->co; + const vec2<T> *b = &se->next->vert->co; + SymEdge<T> *sesym = sym(se); + SymEdge<T> *sesymprev = prev(sesym); + SymEdge<T> *sesymprevsym = sym(sesymprev); + SymEdge<T> *senext = se->next; + CDTVert<T> *v = this->add_vert(vec2<T>::interpolate(*a, *b, lambda)); + CDTEdge<T> *e = this->add_edge(v, se->next->vert, se->face, sesym->face); + sesym->vert = v; + SymEdge<T> *newse = &e->symedges[0]; + SymEdge<T> *newsesym = &e->symedges[1]; + se->next = newse; + newsesym->next = sesym; + newse->next = senext; + newse->rot = sesym; + sesym->rot = newse; + senext->rot = newsesym; + newsesym->rot = sesymprevsym; + sesymprev->next = newsesym; + if (newsesym->vert->symedge == sesym) { + newsesym->vert->symedge = newsesym; + } + add_list_to_input_ids(&e->input_ids, se->edge->input_ids); + return e; +} + +/** + * Delete an edge from the structure. The new combined face on either side of + * the deleted edge will be the one that was e's face. + * There will be now an unused face, marked by setting its deleted flag, + * and an unused #CDTEdge, marked by setting the next and rot pointers of + * its #SymEdges to #nullptr. + * <pre> + * . v2 . + * / \ / \ + * /f|j\ / \ + * / | \ / \ + * | + * A | B A + * \ e| / \ / + * \ | / \ / + * \h|i/ \ / + * . v1 . + * </pre> + * Also handle variant cases where one or both ends + * are attached only to e. + */ +template<typename T> void CDTArrangement<T>::delete_edge(SymEdge<T> *se) +{ + SymEdge<T> *sesym = sym(se); + CDTVert<T> *v1 = se->vert; + CDTVert<T> *v2 = sesym->vert; + CDTFace<T> *aface = se->face; + CDTFace<T> *bface = sesym->face; + SymEdge<T> *f = se->next; + SymEdge<T> *h = prev(se); + SymEdge<T> *i = sesym->next; + SymEdge<T> *j = prev(sesym); + SymEdge<T> *jsym = sym(j); + SymEdge<T> *hsym = sym(h); + bool v1_isolated = (i == se); + bool v2_isolated = (f == sesym); + + if (!v1_isolated) { + h->next = i; + i->rot = hsym; + } + if (!v2_isolated) { + j->next = f; + f->rot = jsym; + } + if (!v1_isolated && !v2_isolated && aface != bface) { + for (SymEdge<T> *k = i; k != f; k = k->next) { + k->face = aface; + } + } + + /* If e was representative symedge for v1 or v2, fix that. */ + if (v1_isolated) { + v1->symedge = nullptr; + } + else if (v1->symedge == se) { + v1->symedge = i; + } + if (v2_isolated) { + v2->symedge = nullptr; + } + else if (v2->symedge == sesym) { + v2->symedge = f; + } + + /* Mark SymEdge as deleted by setting all its pointers to NULL. */ + se->next = se->rot = nullptr; + sesym->next = sesym->rot = nullptr; + if (!v1_isolated && !v2_isolated && aface != bface) { + bface->deleted = true; + if (this->outer_face == bface) { + this->outer_face = aface; + } + } +} + +template<typename T> class SiteInfo { + public: + CDTVert<T> *v; + int orig_index; +}; + +/** + * Compare function for lexicographic sort: x, then y, then index. + */ +template<typename T> bool site_lexicographic_sort(const SiteInfo<T> &a, const SiteInfo<T> &b) +{ + const vec2<T> &co_a = a.v->co; + const vec2<T> &co_b = b.v->co; + if (co_a[0] < co_b[0]) { + return true; + } + if (co_a[0] > co_b[0]) { + return false; + } + if (co_a[1] < co_b[1]) { + return true; + } + if (co_a[1] > co_b[1]) { + return false; + } + return a.orig_index < b.orig_index; +} + +/** + * Find series of equal vertices in the sorted sites array + * and use the vertices merge_to_index to indicate that + * all vertices after the first merge to the first. + */ +template<typename T> void find_site_merges(Array<SiteInfo<T>> &sites) +{ + int n = sites.size(); + for (int i = 0; i < n - 1; ++i) { + int j = i + 1; + while (j < n && sites[j].v->co == sites[i].v->co) { + sites[j].v->merge_to_index = sites[i].orig_index; + ++j; + } + if (j - i > 1) { + i = j - 1; /* j-1 because loop head will add another 1. */ + } + } +} + +template<typename T> inline bool vert_left_of_symedge(CDTVert<T> *v, SymEdge<T> *se) +{ + return orient2d(v->co, se->vert->co, se->next->vert->co) > 0; +} + +template<typename T> inline bool vert_right_of_symedge(CDTVert<T> *v, SymEdge<T> *se) +{ + return orient2d(v->co, se->next->vert->co, se->vert->co) > 0; +} + +/* Is se above basel? */ +template<typename T> +inline bool dc_tri_valid(SymEdge<T> *se, SymEdge<T> *basel, SymEdge<T> *basel_sym) +{ + return orient2d(se->next->vert->co, basel_sym->vert->co, basel->vert->co) > 0; +} + +/** + * Delaunay triangulate sites[start} to sites[end-1]. + * Assume sites are lexicographically sorted by coordinate. + * Return #SymEdge of CCW convex hull at left-most point in *r_le + * and that of right-most point of cw convex null in *r_re. + */ +template<typename T> +void dc_tri(CDTArrangement<T> *cdt, + Array<SiteInfo<T>> &sites, + int start, + int end, + SymEdge<T> **r_le, + SymEdge<T> **r_re) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "DC_TRI start=" << start << " end=" << end << "\n"; + } + int n = end - start; + if (n <= 1) { + *r_le = nullptr; + *r_re = nullptr; + return; + } + + /* Base case: if n <= 3, triangulate directly. */ + if (n <= 3) { + CDTVert<T> *v1 = sites[start].v; + CDTVert<T> *v2 = sites[start + 1].v; + CDTEdge<T> *ea = cdt->add_edge(v1, v2, cdt->outer_face, cdt->outer_face); + ea->symedges[0].next = &ea->symedges[1]; + ea->symedges[1].next = &ea->symedges[0]; + ea->symedges[0].rot = &ea->symedges[0]; + ea->symedges[1].rot = &ea->symedges[1]; + if (n == 2) { + *r_le = &ea->symedges[0]; + *r_re = &ea->symedges[1]; + return; + } + CDTVert<T> *v3 = sites[start + 2].v; + CDTEdge<T> *eb = cdt->add_vert_to_symedge_edge(v3, &ea->symedges[1]); + int orient = orient2d(v1->co, v2->co, v3->co); + if (orient > 0) { + cdt->add_diagonal(&eb->symedges[0], &ea->symedges[0]); + *r_le = &ea->symedges[0]; + *r_re = &eb->symedges[0]; + } + else if (orient < 0) { + cdt->add_diagonal(&ea->symedges[0], &eb->symedges[0]); + *r_le = ea->symedges[0].rot; + *r_re = eb->symedges[0].rot; + } + else { + /* Collinear points. Just return a line. */ + *r_le = &ea->symedges[0]; + *r_re = &eb->symedges[0]; + } + return; + } + /* Recursive case. Do left (L) and right (R) halves seperately, then join. */ + int n2 = n / 2; + BLI_assert(n2 >= 2 && end - (start + n2) >= 2); + SymEdge<T> *ldo; + SymEdge<T> *ldi; + SymEdge<T> *rdi; + SymEdge<T> *rdo; + dc_tri(cdt, sites, start, start + n2, &ldo, &ldi); + dc_tri(cdt, sites, start + n2, end, &rdi, &rdo); + if (dbg_level > 0) { + std::cout << "\nDC_TRI merge step for start=" << start << ", end=" << end << "\n"; + std::cout << "ldo " << ldo << "\n" + << "ldi " << ldi << "\n" + << "rdi " << rdi << "\n" + << "rdo " << rdo << "\n"; + if (dbg_level > 1) { + std::string lab = "dc_tri (" + std::to_string(start) + "," + std::to_string(start + n2) + + ")(" + std::to_string(start + n2) + "," + std::to_string(end) + ")"; + cdt_draw(lab, *cdt); + } + } + /* Find lower common tangent of L and R. */ + for (;;) { + if (vert_left_of_symedge(rdi->vert, ldi)) { + ldi = ldi->next; + } + else if (vert_right_of_symedge(ldi->vert, rdi)) { + rdi = sym(rdi)->rot; /* Previous edge to rdi with same right face. */ + } + else { + break; + } + } + if (dbg_level > 0) { + std::cout << "common lower tangent in between\n" + << "rdi " << rdi << "\n" + << "ldi" << ldi << "\n"; + } + + CDTEdge<T> *ebasel = cdt->connect_separate_parts(sym(rdi)->next, ldi); + SymEdge<T> *basel = &ebasel->symedges[0]; + SymEdge<T> *basel_sym = &ebasel->symedges[1]; + if (dbg_level > 1) { + std::cout << "basel " << basel; + cdt_draw("after basel made", *cdt); + } + if (ldi->vert == ldo->vert) { + ldo = basel_sym; + } + if (rdi->vert == rdo->vert) { + rdo = basel; + } + + /* Merge loop. */ + for (;;) { + /* Locate the first point lcand->next->vert encountered by rising bubble, + * and delete L edges out of basel->next->vert that fail the circle test. */ + SymEdge<T> *lcand = basel_sym->rot; + SymEdge<T> *rcand = basel_sym->next; + if (dbg_level > 1) { + std::cout << "\ntop of merge loop\n"; + std::cout << "lcand " << lcand << "\n" + << "rcand " << rcand << "\n" + << "basel " << basel << "\n"; + } + if (dc_tri_valid(lcand, basel, basel_sym)) { + if (dbg_level > 1) { + std::cout << "found valid lcand\n"; + std::cout << " lcand" << lcand << "\n"; + } + while (incircle(basel_sym->vert->co, + basel->vert->co, + lcand->next->vert->co, + lcand->rot->next->vert->co) > 0.0) { + if (dbg_level > 1) { + std::cout << "incircle says to remove lcand\n"; + std::cout << " lcand" << lcand << "\n"; + } + SymEdge<T> *t = lcand->rot; + cdt->delete_edge(sym(lcand)); + lcand = t; + } + } + /* Symmetrically, locate first R point to be hit and delete R edges. */ + if (dc_tri_valid(rcand, basel, basel_sym)) { + if (dbg_level > 1) { + std::cout << "found valid rcand\n"; + std::cout << " rcand" << rcand << "\n"; + } + while (incircle(basel_sym->vert->co, + basel->vert->co, + rcand->next->vert->co, + sym(rcand)->next->next->vert->co) > 0.0) { + if (dbg_level > 0) { + std::cout << "incircle says to remove rcand\n"; + std::cout << " rcand" << rcand << "\n"; + } + SymEdge<T> *t = sym(rcand)->next; + cdt->delete_edge(rcand); + rcand = t; + } + } + /* If both lcand and rcand are invalid, then basel is the common upper tangent. */ + bool valid_lcand = dc_tri_valid(lcand, basel, basel_sym); + bool valid_rcand = dc_tri_valid(rcand, basel, basel_sym); + if (dbg_level > 0) { + std::cout << "after bubbling up, valid_lcand=" << valid_lcand + << ", valid_rand=" << valid_rcand << "\n"; + std::cout << "lcand" << lcand << "\n" + << "rcand" << rcand << "\n"; + } + if (!valid_lcand && !valid_rcand) { + break; + } + /* The next cross edge to be connected is to either `lcand->next->vert` or `rcand->next->vert`; + * if both are valid, choose the appropriate one using the #incircle test. */ + if (!valid_lcand || + (valid_rcand && + incircle(lcand->next->vert->co, lcand->vert->co, rcand->vert->co, rcand->next->vert->co) > + 0)) { + if (dbg_level > 0) { + std::cout << "connecting rcand\n"; + std::cout << " se1=basel_sym" << basel_sym << "\n"; + std::cout << " se2=rcand->next" << rcand->next << "\n"; + } + ebasel = cdt->add_diagonal(rcand->next, basel_sym); + } + else { + if (dbg_level > 0) { + std::cout << "connecting lcand\n"; + std::cout << " se1=sym(lcand)" << sym(lcand) << "\n"; + std::cout << " se2=basel_sym->next" << basel_sym->next << "\n"; + } + ebasel = cdt->add_diagonal(basel_sym->next, sym(lcand)); + } + basel = &ebasel->symedges[0]; + basel_sym = &ebasel->symedges[1]; + BLI_assert(basel_sym->face == cdt->outer_face); + if (dbg_level > 2) { + cdt_draw("after adding new crossedge", *cdt); + } + } + *r_le = ldo; + *r_re = rdo; + BLI_assert(sym(ldo)->face == cdt->outer_face && rdo->face == cdt->outer_face); +} + +/* Guibas-Stolfi Divide-and_Conquer algorithm. */ +template<typename T> void dc_triangulate(CDTArrangement<T> *cdt, Array<SiteInfo<T>> &sites) +{ + /* Compress sites in place to eliminted verts that merge to others. */ + int i = 0; + int j = 0; + int nsites = sites.size(); + while (j < nsites) { + /* Invariante: sites[0..i-1] have non-merged verts from 0..(j-1) in them. */ + sites[i] = sites[j++]; + if (sites[i].v->merge_to_index < 0) { + i++; + } + } + int n = i; + if (n == 0) { + return; + } + SymEdge<T> *le; + SymEdge<T> *re; + dc_tri(cdt, sites, 0, n, &le, &re); +} + +/** + * Do a Delaunay Triangulation of the points in cdt.verts. + * This is only a first step in the Constrained Delaunay triangulation, + * because it doesn't yet deal with the segment constraints. + * The algorithm used is the Divide & Conquer algorithm from the + * Guibas-Stolfi "Primitives for the Manipulation of General Subdivision + * and the Computation of Voronoi Diagrams" paper. + * The data structure here is similar to but not exactly the same as + * the quad-edge structure described in that paper. + * If T is not exact arithmetic, incircle and CCW tests are done using + * Shewchuk's exact primitives, so that this routine is robust. + * + * As a preprocessing step, we want to merge all vertices that the same. + * This is accomplished by lexicographically + * sorting the coordinates first (which is needed anyway for the D&C algorithm). + * The CDTVerts with merge_to_index not equal to -1 are after this regarded + * as having been merged into the vertex with the corresponding index. + */ +template<typename T> void initial_triangulation(CDTArrangement<T> *cdt) +{ + int n = cdt->verts.size(); + if (n <= 1) { + return; + } + Array<SiteInfo<T>> sites(n); + for (int i = 0; i < n; ++i) { + sites[i].v = cdt->verts[i]; + sites[i].orig_index = i; + } + std::sort(sites.begin(), sites.end(), site_lexicographic_sort<T>); + find_site_merges(sites); + dc_triangulate(cdt, sites); +} + +/** + * Re-triangulates, assuring constrained delaunay condition, + * the pseudo-polygon that cycles from se. + * "pseudo" because a vertex may be repeated. + * See Anglada paper, "An Improved incremental algorithm + * for constructing restricted Delaunay triangulations". + */ +template<typename T> static void re_delaunay_triangulate(CDTArrangement<T> *cdt, SymEdge<T> *se) +{ + if (se->face == cdt->outer_face || sym(se)->face == cdt->outer_face) { + return; + } + /* 'se' is a diagonal just added, and it is base of area to retriangulate (face on its left) */ + int count = 1; + for (SymEdge<T> *ss = se->next; ss != se; ss = ss->next) { + count++; + } + if (count <= 3) { + return; + } + /* First and last are the SymEdges whose verts are first and last off of base, + * continuing from 'se'. */ + SymEdge<T> *first = se->next->next; + /* We want to make a triangle with 'se' as base and some other c as 3rd vertex. */ + CDTVert<T> *a = se->vert; + CDTVert<T> *b = se->next->vert; + CDTVert<T> *c = first->vert; + SymEdge<T> *cse = first; + for (SymEdge<T> *ss = first->next; ss != se; ss = ss->next) { + CDTVert<T> *v = ss->vert; + if (incircle(a->co, b->co, c->co, v->co) > 0) { + c = v; + cse = ss; + } + } + /* Add diagonals necessary to make abc a triangle. */ + CDTEdge<T> *ebc = nullptr; + CDTEdge<T> *eca = nullptr; + if (!exists_edge(b, c)) { + ebc = cdt->add_diagonal(se->next, cse); + } + if (!exists_edge(c, a)) { + eca = cdt->add_diagonal(cse, se); + } + /* Now recurse. */ + if (ebc) { + re_delaunay_triangulate(cdt, &ebc->symedges[1]); + } + if (eca) { + re_delaunay_triangulate(cdt, &eca->symedges[1]); + } +} + +template<typename T> inline int tri_orient(const SymEdge<T> *t) +{ + return orient2d(t->vert->co, t->next->vert->co, t->next->next->vert->co); +} + +/** + * The #CrossData class defines either an endpoint or an intermediate point + * in the path we will take to insert an edge constraint. + * Each such point will either be + * (a) a vertex or + * (b) a fraction lambda (0 < lambda < 1) along some #SymEdge.] + * + * In general, lambda=0 indicates case a and lambda != 0 indicates case be. + * The 'in' edge gives the destination attachment point of a diagonal from the previous crossing, + * and the 'out' edge gives the origin attachment point of a diagonal to the next crossing. + * But in some cases, 'in' and 'out' are undefined or not needed, and will be NULL. + * + * For case (a), 'vert' will be the vertex, and lambda will be 0, and 'in' will be the #SymEdge + * from 'vert' that has as face the one that you go through to get to this vertex. If you go + * exactly along an edge then we set 'in' to NULL, since it won't be needed. The first crossing + * will have 'in' = NULL. We set 'out' to the #SymEdge that has the face we go though to get to the + * next crossing, or, if the next crossing is a case (a), then it is the edge that goes to that + * next vertex. 'out' will be NULL for the last one. + * + * For case (b), vert will be NULL at first, and later filled in with the created split vertex, + * and 'in' will be the #SymEdge that we go through, and lambda will be between 0 and 1, + * the fraction from in's vert to in->next's vert to put the split vertex. + * 'out' is not needed in this case, since the attachment point will be the sym of the first + * half of the split edge. + */ +template<typename T> class CrossData { + public: + T lambda = T(0); + CDTVert<T> *vert; + SymEdge<T> *in; + SymEdge<T> *out; + + CrossData() : lambda(T(0)), vert(nullptr), in(nullptr), out(nullptr) + { + } + CrossData(T l, CDTVert<T> *v, SymEdge<T> *i, SymEdge<T> *o) : lambda(l), vert(v), in(i), out(o) + { + } + CrossData(const CrossData &other) + : lambda(other.lambda), vert(other.vert), in(other.in), out(other.out) + { + } + CrossData(CrossData &&other) noexcept + : lambda(std::move(other.lambda)), + vert(std::move(other.vert)), + in(std::move(other.in)), + out(std::move(other.out)) + { + } + ~CrossData() = default; + CrossData &operator=(const CrossData &other) + { + if (this != &other) { + lambda = other.lambda; + vert = other.vert; + in = other.in; + out = other.out; + } + return *this; + } + CrossData &operator=(CrossData &&other) noexcept + { + lambda = std::move(other.lambda); + vert = std::move(other.vert); + in = std::move(other.in); + out = std::move(other.out); + return *this; + } +}; + +template<typename T> +bool get_next_crossing_from_vert(CDT_state<T> *cdt_state, + CrossData<T> *cd, + CrossData<T> *cd_next, + const CDTVert<T> *v2); + +/** + * As part of finding crossings, we found a case where the next crossing goes through vert v. + * If it came from a previous vert in cd, then cd_out is the edge that leads from that to v. + * Else cd_out can be NULL, because it won't be used. + * Set *cd_next to indicate this. We can set 'in' but not 'out'. We can set the 'out' of the + * current cd. + */ +template<typename T> +void fill_crossdata_for_through_vert(CDTVert<T> *v, + SymEdge<T> *cd_out, + CrossData<T> *cd, + CrossData<T> *cd_next) +{ + SymEdge<T> *se; + + cd_next->lambda = T(0); + cd_next->vert = v; + cd_next->in = NULL; + cd_next->out = NULL; + if (cd->lambda == 0) { + cd->out = cd_out; + } + else { + /* One of the edges in the triangle with edge sym(cd->in) contains v. */ + se = sym(cd->in); + if (se->vert != v) { + se = se->next; + if (se->vert != v) { + se = se->next; + } + } + BLI_assert(se->vert == v); + cd_next->in = se; + } +} + +/** + * As part of finding crossings, we found a case where orient tests say that the next crossing + * is on the #SymEdge t, while intersecting with the ray from \a curco to \a v2. + * Find the intersection point and fill in the #CrossData for that point. + * It may turn out that when doing the intersection, we get an answer that says that + * this case is better handled as through-vertex case instead, so we may do that. + * In the latter case, we want to avoid a situation where the current crossing is on an edge + * and the next will be an endpoint of the same edge. When that happens, we "rewrite history" + * and turn the current crossing into a vert one, and then extend from there. + * + * We cannot fill cd_next's 'out' edge yet, in the case that the next one ends up being a vert + * case. We need to fill in cd's 'out' edge if it was a vert case. + */ +template<typename T> +void fill_crossdata_for_intersect(const vec2<T> &curco, + const CDTVert<T> *v2, + SymEdge<T> *t, + CrossData<T> *cd, + CrossData<T> *cd_next, + const T epsilon) +{ + CDTVert<T> *va = t->vert; + CDTVert<T> *vb = t->next->vert; + CDTVert<T> *vc = t->next->next->vert; + SymEdge<T> *se_vcvb = sym(t->next); + SymEdge<T> *se_vcva = t->next->next; + BLI_assert(se_vcva->vert == vc && se_vcva->next->vert == va); + BLI_assert(se_vcvb->vert == vc && se_vcvb->next->vert == vb); + UNUSED_VARS_NDEBUG(vc); + auto isect = vec2<T>::isect_seg_seg(va->co, vb->co, curco, v2->co); + T &lambda = isect.lambda; + switch (isect.kind) { + case vec2<T>::isect_result::LINE_LINE_CROSS: { +#ifdef WITH_GMP + if (!std::is_same<T, mpq_class>::value) { +#else + if (true) { +#endif + T len_ab = vec2<T>::distance(va->co, vb->co); + if (lambda * len_ab <= epsilon) { + fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); + } + else if ((1 - lambda) * len_ab <= epsilon) { + fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); + } + else { + *cd_next = CrossData<T>(lambda, nullptr, t, nullptr); + if (cd->lambda == 0) { + cd->out = se_vcva; + } + } + } + else { + *cd_next = CrossData<T>(lambda, nullptr, t, nullptr); + if (cd->lambda == 0) { + cd->out = se_vcva; + } + } + break; + } + case vec2<T>::isect_result::LINE_LINE_EXACT: { + if (lambda == 0) { + fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); + } + else if (lambda == 1) { + fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); + } + else { + *cd_next = CrossData<T>(lambda, nullptr, t, nullptr); + if (cd->lambda == 0) { + cd->out = se_vcva; + } + } + break; + } + case vec2<T>::isect_result::LINE_LINE_NONE: { +#ifdef WITH_GMP + if (std::is_same<T, mpq_class>::value) { + BLI_assert(false); + } +#endif + /* It should be very near one end or other of segment. */ + const T middle_lambda = 0.5; + if (lambda <= middle_lambda) { + fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); + } + else { + fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); + } + break; + } + case vec2<T>::isect_result::LINE_LINE_COLINEAR: { + if (vec2<T>::distance_squared(va->co, v2->co) <= vec2<T>::distance_squared(vb->co, v2->co)) { + fill_crossdata_for_through_vert(va, se_vcva, cd, cd_next); + } + else { + fill_crossdata_for_through_vert(vb, se_vcvb, cd, cd_next); + } + break; + } + } +} // namespace blender::meshintersect + +/** + * As part of finding the crossings of a ray to v2, find the next crossing after 'cd', assuming + * 'cd' represents a crossing that goes through a vertex. + * + * We do a rotational scan around cd's vertex, looking for the triangle where the ray from cd->vert + * to v2 goes between the two arms from cd->vert, or where it goes along one of the edges. + */ +template<typename T> +bool get_next_crossing_from_vert(CDT_state<T> *cdt_state, + CrossData<T> *cd, + CrossData<T> *cd_next, + const CDTVert<T> *v2) +{ + SymEdge<T> *tstart = cd->vert->symedge; + SymEdge<T> *t = tstart; + CDTVert<T> *vcur = cd->vert; + bool ok = false; + do { + /* The ray from `vcur` to v2 has to go either between two successive + * edges around `vcur` or exactly along them. This time through the + * loop, check to see if the ray goes along `vcur-va` + * or between `vcur-va` and `vcur-vb`, where va is the end of t + * and vb is the next vertex (on the next rot edge around vcur, but + * should also be the next vert of triangle starting with `vcur-va`. */ + if (t->face != cdt_state->cdt.outer_face && tri_orient(t) < 0) { + BLI_assert(false); /* Shouldn't happen. */ + } + CDTVert<T> *va = t->next->vert; + CDTVert<T> *vb = t->next->next->vert; + int orient1 = orient2d(t->vert->co, va->co, v2->co); + if (orient1 == 0 && in_line<T>(vcur->co, va->co, v2->co)) { + fill_crossdata_for_through_vert(va, t, cd, cd_next); + ok = true; + break; + } + if (t->face != cdt_state->cdt.outer_face) { + int orient2 = orient2d(vcur->co, vb->co, v2->co); + /* Don't handle orient2 == 0 case here: next rotation will get it. */ + if (orient1 > 0 && orient2 < 0) { + /* Segment intersection. */ + t = t->next; + fill_crossdata_for_intersect(vcur->co, v2, t, cd, cd_next, cdt_state->epsilon); + ok = true; + break; + } + } + } while ((t = t->rot) != tstart); + return ok; +} + +/** + * As part of finding the crossings of a ray to `v2`, find the next crossing after 'cd', assuming + * 'cd' represents a crossing that goes through a an edge, not at either end of that edge. + * + * We have the triangle `vb-va-vc`, where `va` and vb are the split edge and `vc` is the third + * vertex on that new side of the edge (should be closer to `v2`). + * The next crossing should be through `vc` or intersecting `vb-vc` or `va-vc`. + */ +template<typename T> +void get_next_crossing_from_edge(CrossData<T> *cd, + CrossData<T> *cd_next, + const CDTVert<T> *v2, + const T epsilon) +{ + CDTVert<T> *va = cd->in->vert; + CDTVert<T> *vb = cd->in->next->vert; + vec2<T> curco = vec2<T>::interpolate(va->co, vb->co, cd->lambda); + SymEdge<T> *se_ac = sym(cd->in)->next; + CDTVert<T> *vc = se_ac->next->vert; + int orient = orient2d(curco, v2->co, vc->co); + if (orient < 0) { + fill_crossdata_for_intersect<T>(curco, v2, se_ac->next, cd, cd_next, epsilon); + } + else if (orient > 0.0) { + fill_crossdata_for_intersect(curco, v2, se_ac, cd, cd_next, epsilon); + } + else { + *cd_next = CrossData<T>{0.0, vc, se_ac->next, nullptr}; + } +} + +constexpr int inline_crossings_size = 128; +template<typename T> +void dump_crossings(const Vector<CrossData<T>, inline_crossings_size> &crossings) +{ + std::cout << "CROSSINGS\n"; + for (int i = 0; i < crossings.size(); ++i) { + std::cout << i << ": "; + const CrossData<T> &cd = crossings[i]; + if (cd.lambda == 0) { + std::cout << "v" << cd.vert->index; + } + else { + std::cout << "lambda=" << cd.lambda; + } + if (cd.in != nullptr) { + std::cout << " in=" << short_se_dump(cd.in); + std::cout << " out=" << short_se_dump(cd.out); + } + std::cout << "\n"; + } +} + +/** + * Add a constrained edge between v1 and v2 to cdt structure. + * This may result in a number of #CDTEdges created, due to intersections + * and partial overlaps with existing cdt vertices and edges. + * Each created #CDTEdge will have input_id added to its input_ids list. + * + * If \a r_edges is not NULL, the #CDTEdges generated or found that go from + * v1 to v2 are put into that linked list, in order. + * + * Assumes that #blender_constrained_delaunay_get_output has not been called yet. + */ +template<typename T> +void add_edge_constraint( + CDT_state<T> *cdt_state, CDTVert<T> *v1, CDTVert<T> *v2, int input_id, LinkNode **r_edges) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nADD EDGE CONSTRAINT\n" << vertname(v1) << " " << vertname(v2) << "\n"; + } + LinkNodePair edge_list = {NULL, NULL}; + + if (r_edges) { + *r_edges = NULL; + } + + /* + * Handle two special cases first: + * 1) The two end vertices are the same (can happen because of merging). + * 2) There is already an edge between v1 and v2. + */ + if (v1 == v2) { + return; + } + SymEdge<T> *t = find_symedge_between_verts(v1, v2); + if (t != nullptr) { + /* Segment already there. */ + add_to_input_ids(&t->edge->input_ids, input_id); + if (r_edges != NULL) { + BLI_linklist_append(&edge_list, t->edge); + *r_edges = edge_list.list; + } + return; + } + + /* + * Fill crossings array with CrossData points for intersection path from v1 to v2. + * + * At every point, the crossings array has the path so far, except that + * the .out field of the last element of it may not be known yet -- if that + * last element is a vertex, then we won't know the output edge until we + * find the next crossing. + * + * To protect against infinite loops, we keep track of which vertices + * we have visited by setting their visit_index to a new visit epoch. + * + * We check a special case first: where the segment is already there in + * one hop. Saves a bunch of orient2d tests in that common case. + */ + int visit = ++cdt_state->visit_count; + Vector<CrossData<T>, inline_crossings_size> crossings; + crossings.append(CrossData<T>(T(0), v1, nullptr, nullptr)); + int n; + while (!((n = crossings.size()) > 0 && crossings[n - 1].vert == v2)) { + crossings.append(CrossData<T>()); + CrossData<T> *cd = &crossings[n - 1]; + CrossData<T> *cd_next = &crossings[n]; + bool ok; + if (crossings[n - 1].lambda == 0) { + ok = get_next_crossing_from_vert(cdt_state, cd, cd_next, v2); + } + else { + get_next_crossing_from_edge(cd, cd_next, v2, cdt_state->epsilon); + ok = true; + } + constexpr int unreasonably_large_crossings = 100000; + if (!ok || crossings.size() == unreasonably_large_crossings) { + /* Shouldn't happen but if does, just bail out. */ + BLI_assert(false); + return; + } + if (crossings[n].lambda == 0) { + if (crossings[n].vert->visit_index == visit) { + /* Shouldn't happen but if it does, just bail out. */ + BLI_assert(false); + return; + } + crossings[n].vert->visit_index = visit; + } + } + + if (dbg_level > 0) { + dump_crossings(crossings); + } + + /* + * Post-process crossings. + * Some crossings may have an intersection crossing followed + * by a vertex crossing that is on the same edge that was just + * intersected. We prefer to go directly from the previous + * crossing directly to the vertex. This may chain backwards. + * + * This loop marks certain crossings as "deleted", by setting + * their lambdas to -1.0. + */ + int ncrossings = crossings.size(); + for (int i = 2; i < ncrossings; ++i) { + CrossData<T> *cd = &crossings[i]; + if (cd->lambda == 0.0) { + CDTVert<T> *v = cd->vert; + int j; + CrossData<T> *cd_prev; + for (j = i - 1; j > 0; --j) { + cd_prev = &crossings[j]; + if ((cd_prev->lambda == 0.0 && cd_prev->vert != v) || + (cd_prev->lambda != 0.0 && cd_prev->in->vert != v && cd_prev->in->next->vert != v)) { + break; + } + cd_prev->lambda = -1.0; /* Mark cd_prev as 'deleted'. */ + } + if (j < i - 1) { + /* Some crossings were deleted. Fix the in and out edges across gap. */ + cd_prev = &crossings[j]; + SymEdge<T> *se; + if (cd_prev->lambda == 0.0) { + se = find_symedge_between_verts(cd_prev->vert, v); + if (se == NULL) { + return; + } + cd_prev->out = se; + cd->in = NULL; + } + else { + se = find_symedge_with_face(v, sym(cd_prev->in)->face); + if (se == NULL) { + return; + } + cd->in = se; + } + } + } + } + + /* + * Insert all intersection points on constrained edges. + */ + for (int i = 0; i < ncrossings; ++i) { + CrossData<T> *cd = &crossings[i]; + if (cd->lambda != 0.0 && cd->lambda != -1.0 && is_constrained_edge(cd->in->edge)) { + CDTEdge<T> *edge = cdt_state->cdt.split_edge(cd->in, cd->lambda); + cd->vert = edge->symedges[0].vert; + } + } + + /* + * Remove any crossed, non-intersected edges. + */ + for (int i = 0; i < ncrossings; ++i) { + CrossData<T> *cd = &crossings[i]; + if (cd->lambda != 0.0 && cd->lambda != -1.0 && !is_constrained_edge(cd->in->edge)) { + cdt_state->cdt.delete_edge(cd->in); + } + } + + /* + * Insert segments for v1->v2. + */ + SymEdge<T> *tstart = crossings[0].out; + for (int i = 1; i < ncrossings; i++) { + CrossData<T> *cd = &crossings[i]; + if (cd->lambda == -1.0) { + continue; /* This crossing was deleted. */ + } + t = NULL; + SymEdge<T> *tnext = t; + CDTEdge<T> *edge; + if (cd->lambda != 0.0) { + if (is_constrained_edge(cd->in->edge)) { + t = cd->vert->symedge; + tnext = sym(t)->next; + } + } + else if (cd->lambda == 0.0) { + t = cd->in; + tnext = cd->out; + if (t == NULL) { + /* Previous non-deleted crossing must also have been a vert, and segment should exist. */ + int j; + CrossData<T> *cd_prev; + for (j = i - 1; j >= 0; j--) { + cd_prev = &crossings[j]; + if (cd_prev->lambda != -1.0) { + break; + } + } + BLI_assert(cd_prev->lambda == 0.0); + BLI_assert(cd_prev->out->next->vert == cd->vert); + edge = cd_prev->out->edge; + add_to_input_ids(&edge->input_ids, input_id); + if (r_edges != NULL) { + BLI_linklist_append(&edge_list, edge); + } + } + } + if (t != NULL) { + if (tstart->next->vert == t->vert) { + edge = tstart->edge; + } + else { + edge = cdt_state->cdt.add_diagonal(tstart, t); + } + add_to_input_ids(&edge->input_ids, input_id); + if (r_edges != NULL) { + BLI_linklist_append(&edge_list, edge); + } + /* Now retriangulate upper and lower gaps. */ + re_delaunay_triangulate(&cdt_state->cdt, &edge->symedges[0]); + re_delaunay_triangulate(&cdt_state->cdt, &edge->symedges[1]); + } + if (i < ncrossings - 1) { + if (tnext != NULL) { + tstart = tnext; + } + } + } + + if (r_edges) { + *r_edges = edge_list.list; + } +} + +/** + * Incrementally add edge input edge as a constraint. This may cause the graph structure + * to change, in cases where the constraints intersect existing edges. + * The code will ensure that #CDTEdge's created will have ids that tie them back + * to the original edge constraint index. + */ +template<typename T> void add_edge_constraints(CDT_state<T> *cdt_state, const CDT_input<T> &input) +{ + int ne = input.edge.size(); + int nv = input.vert.size(); + for (int i = 0; i < ne; i++) { + int iv1 = input.edge[i].first; + int iv2 = input.edge[i].second; + if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) { + /* Ignore invalid indices in edges. */ + continue; + } + CDTVert<T> *v1 = cdt_state->cdt.get_vert_resolve_merge(iv1); + CDTVert<T> *v2 = cdt_state->cdt.get_vert_resolve_merge(iv2); + add_edge_constraint(cdt_state, v1, v2, i, nullptr); + } + cdt_state->face_edge_offset = ne; +} + +/** + * Add face_id to the input_ids lists of all #CDTFace's on the interior of the input face with that + * id. face_symedge is on edge of the boundary of the input face, with assumption that interior is + * on the left of that #SymEdge. + * + * The algorithm is: starting from the #CDTFace for face_symedge, add the face_id and then + * process all adjacent faces where the adjacency isn't across an edge that was a constraint added + * for the boundary of the input face. + * fedge_start..fedge_end is the inclusive range of edge input ids that are for the given face. + * + * Note: if the input face is not CCW oriented, we'll be labeling the outside, not the inside. + * Note 2: if the boundary has self-crossings, this method will arbitrarily pick one of the + * contiguous set of faces enclosed by parts of the boundary, leaving the other such un-tagged. + * This may be a feature instead of a bug if the first contiguous section is most of the face and + * the others are tiny self-crossing triangles at some parts of the boundary. + * On the other hand, if decide we want to handle these in full generality, then will need a more + * complicated algorithm (using "inside" tests and a parity rule) to decide on the interior. + */ +template<typename T> +void add_face_ids( + CDT_state<T> *cdt_state, SymEdge<T> *face_symedge, int face_id, int fedge_start, int fedge_end) +{ + /* Can't loop forever since eventually would visit every face. */ + cdt_state->visit_count++; + int visit = cdt_state->visit_count; + Vector<SymEdge<T> *> stack; + stack.append(face_symedge); + while (!stack.is_empty()) { + SymEdge<T> *se = stack.pop_last(); + CDTFace<T> *face = se->face; + if (face->visit_index == visit) { + continue; + } + face->visit_index = visit; + add_to_input_ids(&face->input_ids, face_id); + SymEdge<T> *se_start = se; + for (se = se->next; se != se_start; se = se->next) { + if (!id_range_in_list(se->edge->input_ids, fedge_start, fedge_end)) { + SymEdge<T> *se_sym = sym(se); + CDTFace<T> *face_other = se_sym->face; + if (face_other->visit_index != visit) { + stack.append(se_sym); + } + } + } + } +} + +/* Return a power of 10 that is greater than or equal to x. */ +static int power_of_10_greater_equal_to(int x) +{ + if (x <= 0) { + return 1; + } + int ans = 1; + BLI_assert(x < INT_MAX / 10); + while (ans < x) { + ans *= 10; + } + return ans; +} + +/** + Incrementally each edge of each input face as an edge constraint. + * The code will ensure that the #CDTEdge's created will have ids that tie them + * back to the original face edge (using a numbering system for those edges + * that starts with cdt->face_edge_offset, and continues with the edges in + * order around each face in turn. And then the next face starts at + * cdt->face_edge_offset beyond the start for the previous face. + */ +template<typename T> void add_face_constraints(CDT_state<T> *cdt_state, const CDT_input<T> &input) +{ + int nv = input.vert.size(); + int nf = input.face.size(); + int fstart = 0; + SymEdge<T> *face_symedge0 = nullptr; + CDTArrangement<T> *cdt = &cdt_state->cdt; + int maxflen = 0; + for (int f = 0; f < nf; f++) { + maxflen = max_ii(maxflen, input.face[f].size()); + } + /* For convenience in debugging, make face_edge_offset be a power of 10. */ + cdt_state->face_edge_offset = power_of_10_greater_equal_to( + max_ii(maxflen, cdt_state->face_edge_offset)); + /* The original_edge encoding scheme doesn't work if the following is false. + * If we really have that many faces and that large a max face length that when multiplied + * together the are >= INT_MAX, then the Delaunay calculation will take unreasonably long anyway. + */ + BLI_assert(INT_MAX / cdt_state->face_edge_offset > nf); + for (int f = 0; f < nf; f++) { + int flen = input.face[f].size(); + if (flen <= 2) { + /* Ignore faces with fewer than 3 vertices. */ + fstart += flen; + continue; + } + int fedge_start = (f + 1) * cdt_state->face_edge_offset; + for (int i = 0; i < flen; i++) { + int face_edge_id = fedge_start + i; + int iv1 = input.face[f][i]; + int iv2 = input.face[f][(i + 1) % flen]; + if (iv1 < 0 || iv1 >= nv || iv2 < 0 || iv2 >= nv) { + /* Ignore face edges with invalid vertices. */ + continue; + } + CDTVert<T> *v1 = cdt->get_vert_resolve_merge(iv1); + CDTVert<T> *v2 = cdt->get_vert_resolve_merge(iv2); + LinkNode *edge_list; + add_edge_constraint(cdt_state, v1, v2, face_edge_id, &edge_list); + /* Set a new face_symedge0 each time since earlier ones may not + * survive later symedge splits. Really, just want the one when + * i == flen -1, but this code guards against that one somehow + * being null. + */ + if (edge_list != nullptr) { + CDTEdge<T> *face_edge = static_cast<CDTEdge<T> *>(edge_list->link); + face_symedge0 = &face_edge->symedges[0]; + if (face_symedge0->vert != v1) { + face_symedge0 = &face_edge->symedges[1]; + BLI_assert(face_symedge0->vert == v1); + } + } + BLI_linklist_free(edge_list, nullptr); + } + int fedge_end = fedge_start + flen - 1; + if (face_symedge0 != nullptr) { + add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end); + } + fstart += flen; + } +} + +/* Delete_edge but try not to mess up outer face. + * Also faces have symedges now, so make sure not + * to mess those up either. */ +template<typename T> void dissolve_symedge(CDT_state<T> *cdt_state, SymEdge<T> *se) +{ + CDTArrangement<T> *cdt = &cdt_state->cdt; + SymEdge<T> *symse = sym(se); + if (symse->face == cdt->outer_face) { + se = sym(se); + symse = sym(se); + } + if (cdt->outer_face->symedge == se || cdt->outer_face->symedge == symse) { + /* Advancing by 2 to get past possible 'sym(se)'. */ + if (se->next->next == se) { + cdt->outer_face->symedge = NULL; + } + else { + cdt->outer_face->symedge = se->next->next; + } + } + else { + if (se->face->symedge == se) { + se->face->symedge = se->next; + } + if (symse->face->symedge == symse) { + symse->face->symedge = symse->next; + } + } + cdt->delete_edge(se); +} + +/** + * Remove all non-constraint edges. + */ +template<typename T> void remove_non_constraint_edges(CDT_state<T> *cdt_state) +{ + for (CDTEdge<T> *e : cdt_state->cdt.edges) { + SymEdge<T> *se = &e->symedges[0]; + if (!is_deleted_edge(e) && !is_constrained_edge(e)) { + dissolve_symedge(cdt_state, se); + } + } +} + +/* + * Remove the non-constraint edges, but leave enough of them so that all of the + * faces that would be #BMesh faces (that is, the faces that have some input representative) + * are valid: they can't have holes, they can't have repeated vertices, and they can't have + * repeated edges. + * + * Not essential, but to make the result look more aesthetically nice, + * remove the edges in order of decreasing length, so that it is more likely that the + * final remaining support edges are short, and therefore likely to make a fairly + * direct path from an outer face to an inner hole face. + */ + +/** + * For sorting edges by decreasing length (squared). + */ +template<typename T> struct EdgeToSort { + T len_squared = T(0); + CDTEdge<T> *e{nullptr}; + + EdgeToSort() = default; + EdgeToSort(const EdgeToSort &other) : len_squared(other.len_squared), e(other.e) + { + } + EdgeToSort(EdgeToSort &&other) noexcept : len_squared(std::move(other.len_squared)), e(other.e) + { + } + ~EdgeToSort() = default; + EdgeToSort &operator=(const EdgeToSort &other) + { + if (this != &other) { + len_squared = other.len_squared; + e = other.e; + } + return *this; + } + EdgeToSort &operator=(EdgeToSort &&other) + { + len_squared = std::move(other.len_squared); + e = other.e; + return *this; + } +}; + +template<typename T> void remove_non_constraint_edges_leave_valid_bmesh(CDT_state<T> *cdt_state) +{ + CDTArrangement<T> *cdt = &cdt_state->cdt; + size_t nedges = cdt->edges.size(); + if (nedges == 0) { + return; + } + Vector<EdgeToSort<T>> dissolvable_edges; + dissolvable_edges.reserve(cdt->edges.size()); + int i = 0; + for (CDTEdge<T> *e : cdt->edges) { + if (!is_deleted_edge(e) && !is_constrained_edge(e)) { + dissolvable_edges.append(EdgeToSort<T>()); + dissolvable_edges[i].e = e; + const vec2<T> &co1 = e->symedges[0].vert->co; + const vec2<T> &co2 = e->symedges[1].vert->co; + dissolvable_edges[i].len_squared = vec2<T>::distance_squared(co1, co2); + i++; + } + } + std::sort(dissolvable_edges.begin(), + dissolvable_edges.end(), + [](const EdgeToSort<T> &a, const EdgeToSort<T> &b) -> bool { + return (a.len_squared < b.len_squared); + }); + for (EdgeToSort<T> &ets : dissolvable_edges) { + CDTEdge<T> *e = ets.e; + SymEdge<T> *se = &e->symedges[0]; + bool dissolve = true; + CDTFace<T> *fleft = se->face; + CDTFace<T> *fright = sym(se)->face; + if (fleft != cdt->outer_face && fright != cdt->outer_face && + (fleft->input_ids != nullptr || fright->input_ids != nullptr)) { + /* Is there another #SymEdge with same left and right faces? + * Or is there a vertex not part of e touching the same left and right faces? */ + for (SymEdge<T> *se2 = se->next; dissolve && se2 != se; se2 = se2->next) { + if (sym(se2)->face == fright || + (se2->vert != se->next->vert && vert_touches_face(se2->vert, fright))) { + dissolve = false; + } + } + } + + if (dissolve) { + dissolve_symedge(cdt_state, se); + } + } +} + +template<typename T> void remove_outer_edges_until_constraints(CDT_state<T> *cdt_state) +{ + // LinkNode *fstack = NULL; + // SymEdge *se, *se_start; + // CDTFace *f, *fsym; + int visit = ++cdt_state->visit_count; + + cdt_state->cdt.outer_face->visit_index = visit; + /* Walk around outer face, adding faces on other side of dissolvable edges to stack. */ + Vector<CDTFace<T> *> fstack; + SymEdge<T> *se_start = cdt_state->cdt.outer_face->symedge; + SymEdge<T> *se = se_start; + do { + if (!is_constrained_edge(se->edge)) { + CDTFace<T> *fsym = sym(se)->face; + if (fsym->visit_index != visit) { + fstack.append(fsym); + } + } + } while ((se = se->next) != se_start); + + while (!fstack.is_empty()) { + LinkNode *to_dissolve = nullptr; + bool dissolvable; + CDTFace<T> *f = fstack.pop_last(); + if (f->visit_index == visit) { + continue; + } + BLI_assert(f != cdt_state->cdt.outer_face); + f->visit_index = visit; + se_start = se = f->symedge; + do { + dissolvable = !is_constrained_edge(se->edge); + if (dissolvable) { + CDTFace<T> *fsym = sym(se)->face; + if (fsym->visit_index != visit) { + fstack.append(fsym); + } + else { + BLI_linklist_prepend(&to_dissolve, se); + } + } + se = se->next; + } while (se != se_start); + while (to_dissolve != NULL) { + se = static_cast<SymEdge<T> *>(BLI_linklist_pop(&to_dissolve)); + if (se->next != NULL) { + dissolve_symedge(cdt_state, se); + } + } + } +} + +/** + * Remove edges and merge faces to get desired output, as per options. + * \note the cdt cannot be further changed after this. + */ +template<typename T> +void prepare_cdt_for_output(CDT_state<T> *cdt_state, const CDT_output_type output_type) +{ + CDTArrangement<T> *cdt = &cdt_state->cdt; + if (cdt->edges.is_empty()) { + return; + } + + /* Make sure all non-deleted faces have a symedge. */ + for (CDTEdge<T> *e : cdt->edges) { + if (!is_deleted_edge(e)) { + if (e->symedges[0].face->symedge == nullptr) { + e->symedges[0].face->symedge = &e->symedges[0]; + } + if (e->symedges[1].face->symedge == nullptr) { + e->symedges[1].face->symedge = &e->symedges[1]; + } + } + } + + if (output_type == CDT_CONSTRAINTS) { + remove_non_constraint_edges(cdt_state); + } + else if (output_type == CDT_CONSTRAINTS_VALID_BMESH) { + remove_non_constraint_edges_leave_valid_bmesh(cdt_state); + } + else if (output_type == CDT_INSIDE) { + remove_outer_edges_until_constraints(cdt_state); + } +} + +template<typename T> +CDT_result<T> get_cdt_output(CDT_state<T> *cdt_state, + const CDT_input<T> UNUSED(input), + CDT_output_type output_type) +{ + prepare_cdt_for_output(cdt_state, output_type); + CDT_result<T> result; + CDTArrangement<T> *cdt = &cdt_state->cdt; + result.face_edge_offset = cdt_state->face_edge_offset; + + /* All verts without a merge_to_index will be output. + * vert_to_output_map[i] will hold the output vertex index + * corresponding to the vert in position i in cdt->verts. + * This first loop sets vert_to_output_map for un-merged verts. */ + int verts_size = cdt->verts.size(); + Array<int> vert_to_output_map(verts_size); + int nv = 0; + for (int i = 0; i < verts_size; ++i) { + CDTVert<T> *v = cdt->verts[i]; + if (v->merge_to_index == -1) { + vert_to_output_map[i] = nv; + ++nv; + } + } + if (nv <= 0) { + return result; + } + /* Now we can set vert_to_output_map for merged verts, + * and also add the input indices of merged verts to the input_ids + * list of the merge target if they were an original input id. */ + if (nv < verts_size) { + for (int i = 0; i < verts_size; ++i) { + CDTVert<T> *v = cdt->verts[i]; + if (v->merge_to_index != -1) { + if (i < cdt_state->input_vert_tot) { + add_to_input_ids(&cdt->verts[v->merge_to_index]->input_ids, i); + } + vert_to_output_map[i] = vert_to_output_map[v->merge_to_index]; + } + } + } + result.vert = Array<vec2<T>>(nv); + result.vert_orig = Array<Vector<int>>(nv); + int i_out = 0; + for (int i = 0; i < verts_size; ++i) { + CDTVert<T> *v = cdt->verts[i]; + if (v->merge_to_index == -1) { + result.vert[i_out] = v->co; + if (i < cdt_state->input_vert_tot) { + result.vert_orig[i_out].append(i); + } + for (LinkNode *ln = v->input_ids; ln; ln = ln->next) { + result.vert_orig[i_out].append(POINTER_AS_INT(ln->link)); + } + ++i_out; + } + } + + /* All non-deleted edges will be output. */ + int ne = std::count_if(cdt->edges.begin(), cdt->edges.end(), [](const CDTEdge<T> *e) -> bool { + return !is_deleted_edge(e); + }); + result.edge = Array<std::pair<int, int>>(ne); + result.edge_orig = Array<Vector<int>>(ne); + int e_out = 0; + for (const CDTEdge<T> *e : cdt->edges) { + if (!is_deleted_edge(e)) { + int vo1 = vert_to_output_map[e->symedges[0].vert->index]; + int vo2 = vert_to_output_map[e->symedges[1].vert->index]; + result.edge[e_out] = std::pair<int, int>(vo1, vo2); + for (LinkNode *ln = e->input_ids; ln; ln = ln->next) { + result.edge_orig[e_out].append(POINTER_AS_INT(ln->link)); + } + ++e_out; + } + } + + /* All non-deleted, non-outer faces will be output. */ + int nf = std::count_if(cdt->faces.begin(), cdt->faces.end(), [=](const CDTFace<T> *f) -> bool { + return !f->deleted && f != cdt->outer_face; + }); + result.face = Array<Vector<int>>(nf); + result.face_orig = Array<Vector<int>>(nf); + int f_out = 0; + for (const CDTFace<T> *f : cdt->faces) { + if (!f->deleted && f != cdt->outer_face) { + SymEdge<T> *se = f->symedge; + BLI_assert(se != nullptr); + SymEdge<T> *se_start = se; + do { + result.face[f_out].append(vert_to_output_map[se->vert->index]); + se = se->next; + } while (se != se_start); + for (LinkNode *ln = f->input_ids; ln; ln = ln->next) { + result.face_orig[f_out].append(POINTER_AS_INT(ln->link)); + } + ++f_out; + } + } + return result; +} + +/** + * Add all the input verts into cdt. This will deduplicate, + * setting vertices merge_to_index to show merges. + */ +template<typename T> void add_input_verts(CDT_state<T> *cdt_state, const CDT_input<T> &input) +{ + for (int i = 0; i < cdt_state->input_vert_tot; ++i) { + cdt_state->cdt.add_vert(input.vert[i]); + } +} + +template<typename T> +CDT_result<T> delaunay_calc(const CDT_input<T> &input, CDT_output_type output_type) +{ + int nv = input.vert.size(); + int ne = input.edge.size(); + int nf = input.face.size(); + CDT_state<T> cdt_state(nv, ne, nf, input.epsilon); + add_input_verts(&cdt_state, input); + initial_triangulation(&cdt_state.cdt); + add_edge_constraints(&cdt_state, input); + add_face_constraints(&cdt_state, input); + return get_cdt_output(&cdt_state, input, output_type); +} + +blender::meshintersect::CDT_result<double> delaunay_2d_calc(const CDT_input<double> &input, + CDT_output_type output_type) +{ + return delaunay_calc(input, output_type); +} + +#ifdef WITH_GMP +blender::meshintersect::CDT_result<mpq_class> delaunay_2d_calc(const CDT_input<mpq_class> &input, + CDT_output_type output_type) +{ + return delaunay_calc(input, output_type); +} +#endif + +} /* namespace blender::meshintersect */ + +/* C interface. */ + +/** + This function uses the double version of #CDT::delaunay_calc. + * Almost all of the work here is to convert between C++ #Arrays<Vector<int>> + * and a C version that linearizes all the elements and uses a "start" + * and "len" array to say where the individual vectors start and how + * long they are. + */ +extern "C" ::CDT_result *BLI_delaunay_2d_cdt_calc(const ::CDT_input *input, + const CDT_output_type output_type) +{ + blender::meshintersect::CDT_input<double> in; + in.vert = blender::Array<blender::meshintersect::vec2<double>>(input->verts_len); + in.edge = blender::Array<std::pair<int, int>>(input->edges_len); + in.face = blender::Array<blender::Vector<int>>(input->faces_len); + for (int v = 0; v < input->verts_len; ++v) { + double x = static_cast<double>(input->vert_coords[v][0]); + double y = static_cast<double>(input->vert_coords[v][1]); + in.vert[v] = blender::meshintersect::vec2<double>(x, y); + } + for (int e = 0; e < input->edges_len; ++e) { + in.edge[e] = std::pair<int, int>(input->edges[e][0], input->edges[e][1]); + } + for (int f = 0; f < input->faces_len; ++f) { + in.face[f] = blender::Vector<int>(input->faces_len_table[f]); + int fstart = input->faces_start_table[f]; + for (int j = 0; j < input->faces_len_table[f]; ++j) { + in.face[f][j] = input->faces[fstart + j]; + } + } + in.epsilon = static_cast<double>(input->epsilon); + + blender::meshintersect::CDT_result<double> res = blender::meshintersect::delaunay_2d_calc( + in, output_type); + + ::CDT_result *output = static_cast<::CDT_result *>(MEM_mallocN(sizeof(*output), __func__)); + int nv = output->verts_len = res.vert.size(); + int ne = output->edges_len = res.edge.size(); + int nf = output->faces_len = res.face.size(); + int tot_v_orig = 0; + int tot_e_orig = 0; + int tot_f_orig = 0; + int tot_f_lens = 0; + for (int v = 0; v < nv; ++v) { + tot_v_orig += res.vert_orig[v].size(); + } + for (int e = 0; e < ne; ++e) { + tot_e_orig += res.edge_orig[e].size(); + } + for (int f = 0; f < nf; ++f) { + tot_f_orig += res.face_orig[f].size(); + tot_f_lens += res.face[f].size(); + } + + output->vert_coords = static_cast<decltype(output->vert_coords)>( + MEM_malloc_arrayN(nv, sizeof(output->vert_coords[0]), __func__)); + output->verts_orig = static_cast<int *>(MEM_malloc_arrayN(tot_v_orig, sizeof(int), __func__)); + output->verts_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(nv, sizeof(int), __func__)); + output->verts_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nv, sizeof(int), __func__)); + output->edges = static_cast<decltype(output->edges)>( + MEM_malloc_arrayN(ne, sizeof(output->edges[0]), __func__)); + output->edges_orig = static_cast<int *>(MEM_malloc_arrayN(tot_e_orig, sizeof(int), __func__)); + output->edges_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(ne, sizeof(int), __func__)); + output->edges_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(ne, sizeof(int), __func__)); + output->faces = static_cast<int *>(MEM_malloc_arrayN(tot_f_lens, sizeof(int), __func__)); + output->faces_start_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); + output->faces_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); + output->faces_orig = static_cast<int *>(MEM_malloc_arrayN(tot_f_orig, sizeof(int), __func__)); + output->faces_orig_start_table = static_cast<int *>( + MEM_malloc_arrayN(nf, sizeof(int), __func__)); + output->faces_orig_len_table = static_cast<int *>(MEM_malloc_arrayN(nf, sizeof(int), __func__)); + + int v_orig_index = 0; + for (int v = 0; v < nv; ++v) { + output->vert_coords[v][0] = static_cast<float>(res.vert[v][0]); + output->vert_coords[v][1] = static_cast<float>(res.vert[v][1]); + int this_start = v_orig_index; + output->verts_orig_start_table[v] = this_start; + for (int j : res.vert_orig[v].index_range()) { + output->verts_orig[v_orig_index++] = res.vert_orig[v][j]; + } + output->verts_orig_len_table[v] = v_orig_index - this_start; + } + int e_orig_index = 0; + for (int e = 0; e < ne; ++e) { + output->edges[e][0] = res.edge[e].first; + output->edges[e][1] = res.edge[e].second; + int this_start = e_orig_index; + output->edges_orig_start_table[e] = this_start; + for (int j : res.edge_orig[e].index_range()) { + output->edges_orig[e_orig_index++] = res.edge_orig[e][j]; + } + output->edges_orig_len_table[e] = e_orig_index - this_start; + } + int f_orig_index = 0; + int f_index = 0; + for (int f = 0; f < nf; ++f) { + output->faces_start_table[f] = f_index; + int flen = res.face[f].size(); + output->faces_len_table[f] = flen; + for (int j = 0; j < flen; ++j) { + output->faces[f_index++] = res.face[f][j]; + } + int this_start = f_orig_index; + output->faces_orig_start_table[f] = this_start; + for (int k : res.face_orig[f].index_range()) { + output->faces_orig[f_orig_index++] = res.face_orig[f][k]; + } + output->faces_orig_len_table[f] = f_orig_index - this_start; + } + return output; +} + +extern "C" void BLI_delaunay_2d_cdt_free(::CDT_result *result) +{ + MEM_freeN(result->vert_coords); + MEM_freeN(result->edges); + MEM_freeN(result->faces); + MEM_freeN(result->faces_start_table); + MEM_freeN(result->faces_len_table); + MEM_freeN(result->verts_orig); + MEM_freeN(result->verts_orig_start_table); + MEM_freeN(result->verts_orig_len_table); + MEM_freeN(result->edges_orig); + MEM_freeN(result->edges_orig_start_table); + MEM_freeN(result->edges_orig_len_table); + MEM_freeN(result->faces_orig); + MEM_freeN(result->faces_orig_start_table); + MEM_freeN(result->faces_orig_len_table); + MEM_freeN(result); +} diff --git a/source/blender/blenlib/intern/math_boolean.cc b/source/blender/blenlib/intern/math_boolean.cc new file mode 100644 index 00000000000..2210911ad9c --- /dev/null +++ b/source/blender/blenlib/intern/math_boolean.cc @@ -0,0 +1,2533 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + */ + +#include "BLI_double2.hh" +#include "BLI_double3.hh" +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_hash.hh" +#include "BLI_math_boolean.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mpq2.hh" +#include "BLI_mpq3.hh" +#include "BLI_span.hh" +#include "BLI_utildefines.h" + +namespace blender { + +#ifdef WITH_GMP +/** + * Return +1 if a, b, c are in CCW order around a circle in the plane. + * Return -1 if they are in CW order, and 0 if they are in line. + */ +int orient2d(const mpq2 &a, const mpq2 &b, const mpq2 &c) +{ + mpq_class detleft = (a[0] - c[0]) * (b[1] - c[1]); + mpq_class detright = (a[1] - c[1]) * (b[0] - c[0]); + mpq_class det = detleft - detright; + return sgn(det); +} + +/** + Return +1 if d is in the oriented circle through a, b, and c. + * The oriented circle goes CCW through a, b, and c. + * Return -1 if d is outside, and 0 if it is on the circle. + */ +int incircle(const mpq2 &a, const mpq2 &b, const mpq2 &c, const mpq2 &d) +{ + mpq_class adx = a[0] - d[0]; + mpq_class bdx = b[0] - d[0]; + mpq_class cdx = c[0] - d[0]; + mpq_class ady = a[1] - d[1]; + mpq_class bdy = b[1] - d[1]; + mpq_class cdy = c[1] - d[1]; + + mpq_class bdxcdy = bdx * cdy; + mpq_class cdxbdy = cdx * bdy; + mpq_class alift = adx * adx + ady * ady; + + mpq_class cdxady = cdx * ady; + mpq_class adxcdy = adx * cdy; + mpq_class blift = bdx * bdx + bdy * bdy; + + mpq_class adxbdy = adx * bdy; + mpq_class bdxady = bdx * ady; + mpq_class clift = cdx * cdx + cdy * cdy; + + mpq_class det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + + clift * (adxbdy - bdxady); + return sgn(det); +} + +/** + * Return +1 if d is below the plane containing a, b, c (which appear + * CCW when viewed from above the plane). + * Return -1 if d is above the plane. + * Return 0 if it is on the plane. + */ +int orient3d(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &d) +{ + mpq_class adx = a[0] - d[0]; + mpq_class bdx = b[0] - d[0]; + mpq_class cdx = c[0] - d[0]; + mpq_class ady = a[1] - d[1]; + mpq_class bdy = b[1] - d[1]; + mpq_class cdy = c[1] - d[1]; + mpq_class adz = a[2] - d[2]; + mpq_class bdz = b[2] - d[2]; + mpq_class cdz = c[2] - d[2]; + + mpq_class bdxcdy = bdx * cdy; + mpq_class cdxbdy = cdx * bdy; + + mpq_class cdxady = cdx * ady; + mpq_class adxcdy = adx * cdy; + + mpq_class adxbdy = adx * bdy; + mpq_class bdxady = bdx * ady; + + mpq_class det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady); + return sgn(det); +} +#endif /* WITH_GMP */ + +/** + * For double versions of orient and incircle functions, use robust predicates + * that give exact answers for double inputs. + * First, encapsulate functions frm Jonathan Shewchuk's implementation. + * After this namespace, see the implementation of the double3 primitives. + */ +namespace robust_pred { + +/* Using Shewchuk's file here, edited to removed unneeded functions, + * change REAL to double everywhere, added const to some arguments, + * and to export only the following declared non-static functions. + * + * Since this is C++, an instantiated singleton class is used to make + * sure that exactinit() is called once. + * (Because of undefinedness of when this is called in initialization of all + * modules, other modules shouldn't use these functions in initialization.) + */ + +void exactinit(); +double orient2dfast(const double *pa, const double *pb, const double *pc); +double orient2d(const double *pa, const double *pb, const double *pc); +double orient3dfast(const double *pa, const double *pb, const double *pc, const double *pd); +double orient3d(const double *pa, const double *pb, const double *pc, const double *pd); +double incirclefast(const double *pa, const double *pb, const double *pc, const double *pd); +double incircle(const double *pa, const double *pb, const double *pc, const double *pd); +double inspherefast( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe); +double insphere( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe); + +class RobustInitCaller { + public: + RobustInitCaller() + { + exactinit(); + } +}; + +static RobustInitCaller init_caller; + +/* Routines for Arbitrary Precision Floating-point Arithmetic + * and Fast Robust Geometric Predicates + * (predicates.c) + * + * May 18, 1996 + * + * Placed in the public domain by + * Jonathan Richard Shewchuk + * School of Computer Science + * Carnegie Mellon University + * 5000 Forbes Avenue + * Pittsburgh, Pennsylvania 15213-3891 + * jrs@cs.cmu.edu + * + * This file contains C implementation of algorithms for exact addition + * and multiplication of floating-point numbers, and predicates for + * robustly performing the orientation and incircle tests used in + * computational geometry. The algorithms and underlying theory are + * described in Jonathan Richard Shewchuk. "Adaptive Precision Floating- + * Point Arithmetic and Fast Robust Geometric Predicates." Technical + * Report CMU-CS-96-140, School of Computer Science, Carnegie Mellon + * University, Pittsburgh, Pennsylvania, May 1996. (Submitted to + * Discrete & Computational Geometry.) + * + * This file, the paper listed above, and other information are available + * from the Web page http://www.cs.cmu.edu/~quake/robust.html . + * + * + * Using this code: + * + * First, read the short or long version of the paper (from the Web page above). + * + * Be sure to call #exactinit() once, before calling any of the arithmetic + * functions or geometric predicates. Also be sure to turn on the + * optimizer when compiling this file. + */ + +/* On some machines, the exact arithmetic routines might be defeated by the + * use of internal extended precision floating-point registers. Sometimes + * this problem can be fixed by defining certain values to be volatile, + * thus forcing them to be stored to memory and rounded off. This isn't + * a great solution, though, as it slows the arithmetic down. + * + * To try this out, write "#define INEXACT volatile" below. Normally, + * however, INEXACT should be defined to be nothing. ("#define INEXACT".) + */ + +#define INEXACT /* Nothing */ +/* #define INEXACT volatile */ + +/* Which of the following two methods of finding the absolute values is + * fastest is compiler-dependent. A few compilers can inline and optimize + * the fabs() call; but most will incur the overhead of a function call, + * which is disastrously slow. A faster way on IEEE machines might be to + * mask the appropriate bit, but that's difficult to do in C. + */ + +#define Absolute(a) ((a) >= 0.0 ? (a) : -(a)) +/* #define Absolute(a) fabs(a) */ + +/* Many of the operations are broken up into two pieces, a main part that + * performs an approximate operation, and a "tail" that computes the + * round-off error of that operation. + * + * The operations Fast_Two_Sum(), Fast_Two_Diff(), Two_Sum(), Two_Diff(), + * Split(), and Two_Product() are all implemented as described in the + * reference. Each of these macros requires certain variables to be + * defined in the calling routine. The variables `bvirt', `c', `abig', + * `_i', `_j', `_k', `_l', `_m', and `_n' are declared `INEXACT' because + * they store the result of an operation that may incur round-off error. + * The input parameter `x' (or the highest numbered `x_' parameter) must + * also be declared `INEXACT'. + */ + +#define Fast_Two_Sum_Tail(a, b, x, y) \ + bvirt = x - a; \ + y = b - bvirt + +#define Fast_Two_Sum(a, b, x, y) \ + x = (double)(a + b); \ + Fast_Two_Sum_Tail(a, b, x, y) + +#define Fast_Two_Diff_Tail(a, b, x, y) \ + bvirt = a - x; \ + y = bvirt - b + +#define Fast_Two_Diff(a, b, x, y) \ + x = (double)(a - b); \ + Fast_Two_Diff_Tail(a, b, x, y) + +#define Two_Sum_Tail(a, b, x, y) \ + bvirt = (double)(x - a); \ + avirt = x - bvirt; \ + bround = b - bvirt; \ + around = a - avirt; \ + y = around + bround + +#define Two_Sum(a, b, x, y) \ + x = (double)(a + b); \ + Two_Sum_Tail(a, b, x, y) + +#define Two_Diff_Tail(a, b, x, y) \ + bvirt = (double)(a - x); \ + avirt = x + bvirt; \ + bround = bvirt - b; \ + around = a - avirt; \ + y = around + bround + +#define Two_Diff(a, b, x, y) \ + x = (double)(a - b); \ + Two_Diff_Tail(a, b, x, y) + +#define Split(a, ahi, alo) \ + c = (double)(splitter * a); \ + abig = (double)(c - a); \ + ahi = c - abig; \ + alo = a - ahi + +#define Two_Product_Tail(a, b, x, y) \ + Split(a, ahi, alo); \ + Split(b, bhi, blo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product(a, b, x, y) \ + x = (double)(a * b); \ + Two_Product_Tail(a, b, x, y) + +#define Two_Product_Presplit(a, b, bhi, blo, x, y) \ + x = (double)(a * b); \ + Split(a, ahi, alo); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Two_Product_2Presplit(a, ahi, alo, b, bhi, blo, x, y) \ + x = (double)(a * b); \ + err1 = x - (ahi * bhi); \ + err2 = err1 - (alo * bhi); \ + err3 = err2 - (ahi * blo); \ + y = (alo * blo) - err3 + +#define Square_Tail(a, x, y) \ + Split(a, ahi, alo); \ + err1 = x - (ahi * ahi); \ + err3 = err1 - ((ahi + ahi) * alo); \ + y = (alo * alo) - err3 + +#define Square(a, x, y) \ + x = (double)(a * a); \ + Square_Tail(a, x, y) + +#define Two_One_Sum(a1, a0, b, x2, x1, x0) \ + Two_Sum(a0, b, _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_One_Diff(a1, a0, b, x2, x1, x0) \ + Two_Diff(a0, b, _i, x0); \ + Two_Sum(a1, _i, x2, x1) + +#define Two_Two_Sum(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b0, _j, _0, x0); \ + Two_One_Sum(_j, _0, b1, x3, x2, x1) + +#define Two_Two_Diff(a1, a0, b1, b0, x3, x2, x1, x0) \ + Two_One_Diff(a1, a0, b0, _j, _0, x0); \ + Two_One_Diff(_j, _0, b1, x3, x2, x1) + +#define Four_One_Sum(a3, a2, a1, a0, b, x4, x3, x2, x1, x0) \ + Two_One_Sum(a1, a0, b, _j, x1, x0); \ + Two_One_Sum(a3, a2, _j, x4, x3, x2) + +#define Four_Two_Sum(a3, a2, a1, a0, b1, b0, x5, x4, x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b0, _k, _2, _1, _0, x0); \ + Four_One_Sum(_k, _2, _1, _0, b1, x5, x4, x3, x2, x1) + +#define Four_Four_Sum(a3, a2, a1, a0, b4, b3, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \ + Four_Two_Sum(a3, a2, a1, a0, b1, b0, _l, _2, _1, _0, x1, x0); \ + Four_Two_Sum(_l, _2, _1, _0, b4, b3, x7, x6, x5, x4, x3, x2) + +#define Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b, x8, x7, x6, x5, x4, x3, x2, x1, x0) \ + Four_One_Sum(a3, a2, a1, a0, b, _j, x3, x2, x1, x0); \ + Four_One_Sum(a7, a6, a5, a4, _j, x8, x7, x6, x5, x4) + +#define Eight_Two_Sum( \ + a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, x9, x8, x7, x6, x5, x4, x3, x2, x1, x0) \ + Eight_One_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b0, _k, _6, _5, _4, _3, _2, _1, _0, x0); \ + Eight_One_Sum(_k, _6, _5, _4, _3, _2, _1, _0, b1, x9, x8, x7, x6, x5, x4, x3, x2, x1) + +#define Eight_Four_Sum(a7, \ + a6, \ + a5, \ + a4, \ + a3, \ + a2, \ + a1, \ + a0, \ + b4, \ + b3, \ + b1, \ + b0, \ + x11, \ + x10, \ + x9, \ + x8, \ + x7, \ + x6, \ + x5, \ + x4, \ + x3, \ + x2, \ + x1, \ + x0) \ + Eight_Two_Sum(a7, a6, a5, a4, a3, a2, a1, a0, b1, b0, _l, _6, _5, _4, _3, _2, _1, _0, x1, x0); \ + Eight_Two_Sum(_l, _6, _5, _4, _3, _2, _1, _0, b4, b3, x11, x10, x9, x8, x7, x6, x5, x4, x3, x2) + +#define Two_One_Product(a1, a0, b, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, x3, x2) + +#define Four_One_Product(a3, a2, a1, a0, b, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(b, bhi, blo); \ + Two_Product_Presplit(a0, b, bhi, blo, _i, x0); \ + Two_Product_Presplit(a1, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x1); \ + Fast_Two_Sum(_j, _k, _i, x2); \ + Two_Product_Presplit(a2, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x3); \ + Fast_Two_Sum(_j, _k, _i, x4); \ + Two_Product_Presplit(a3, b, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, x5); \ + Fast_Two_Sum(_j, _k, x7, x6) + +#define Two_Two_Product(a1, a0, b1, b0, x7, x6, x5, x4, x3, x2, x1, x0) \ + Split(a0, a0hi, a0lo); \ + Split(b0, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b0, bhi, blo, _i, x0); \ + Split(a1, a1hi, a1lo); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b0, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _k, _1); \ + Fast_Two_Sum(_j, _k, _l, _2); \ + Split(b1, bhi, blo); \ + Two_Product_2Presplit(a0, a0hi, a0lo, b1, bhi, blo, _i, _0); \ + Two_Sum(_1, _0, _k, x1); \ + Two_Sum(_2, _k, _j, _1); \ + Two_Sum(_l, _j, _m, _2); \ + Two_Product_2Presplit(a1, a1hi, a1lo, b1, bhi, blo, _j, _0); \ + Two_Sum(_i, _0, _n, _0); \ + Two_Sum(_1, _0, _i, x2); \ + Two_Sum(_2, _i, _k, _1); \ + Two_Sum(_m, _k, _l, _2); \ + Two_Sum(_j, _n, _k, _0); \ + Two_Sum(_1, _0, _j, x3); \ + Two_Sum(_2, _j, _i, _1); \ + Two_Sum(_l, _i, _m, _2); \ + Two_Sum(_1, _k, _i, x4); \ + Two_Sum(_2, _i, _k, x5); \ + Two_Sum(_m, _k, x7, x6) + +#define Two_Square(a1, a0, x5, x4, x3, x2, x1, x0) \ + Square(a0, _j, x0); \ + _0 = a0 + a0; \ + Two_Product(a1, _0, _k, _1); \ + Two_One_Sum(_k, _1, _j, _l, _2, x1); \ + Square(a1, _j, _1); \ + Two_Two_Sum(_j, _1, _l, _2, x5, x4, x3, x2) + +static double splitter; /* = 2^ceiling(p / 2) + 1. Used to split floats in half. */ +static double epsilon; /* = 2^(-p). Used to estimate round-off errors. */ +/* A set of coefficients used to calculate maximum round-off errors. */ +static double resulterrbound; +static double ccwerrboundA, ccwerrboundB, ccwerrboundC; +static double o3derrboundA, o3derrboundB, o3derrboundC; +static double iccerrboundA, iccerrboundB, iccerrboundC; +static double isperrboundA, isperrboundB, isperrboundC; + +/** + * exactinit() Initialize the variables used for exact arithmetic. + * + * `epsilon' is the largest power of two such that 1.0 + epsilon = 1.0 in + * floating-point arithmetic. `epsilon' bounds the relative round-off + * error. It is used for floating-point error analysis. + * + * `splitter' is used to split floating-point numbers into two half- + * length significands for exact multiplication. + * + * I imagine that a highly optimizing compiler might be too smart for its + * own good, and somehow cause this routine to fail, if it pretends that + * floating-point arithmetic is too much like real arithmetic. + * + * Don't change this routine unless you fully understand it. + */ + +void exactinit() +{ + double half; + double check, lastcheck; + int every_other; + + every_other = 1; + half = 0.5; + epsilon = 1.0; + splitter = 1.0; + check = 1.0; + /* Repeatedly divide `epsilon' by two until it is too small to add to + * one without causing round-off. (Also check if the sum is equal to + * the previous sum, for machines that round up instead of using exact + * rounding. Not that this library will work on such machines anyway. */ + do { + lastcheck = check; + epsilon *= half; + if (every_other) { + splitter *= 2.0; + } + every_other = !every_other; + check = 1.0 + epsilon; + } while ((check != 1.0) && (check != lastcheck)); + splitter += 1.0; + + /* Error bounds for orientation and #incircle tests. */ + resulterrbound = (3.0 + 8.0 * epsilon) * epsilon; + ccwerrboundA = (3.0 + 16.0 * epsilon) * epsilon; + ccwerrboundB = (2.0 + 12.0 * epsilon) * epsilon; + ccwerrboundC = (9.0 + 64.0 * epsilon) * epsilon * epsilon; + o3derrboundA = (7.0 + 56.0 * epsilon) * epsilon; + o3derrboundB = (3.0 + 28.0 * epsilon) * epsilon; + o3derrboundC = (26.0 + 288.0 * epsilon) * epsilon * epsilon; + iccerrboundA = (10.0 + 96.0 * epsilon) * epsilon; + iccerrboundB = (4.0 + 48.0 * epsilon) * epsilon; + iccerrboundC = (44.0 + 576.0 * epsilon) * epsilon * epsilon; + isperrboundA = (16.0 + 224.0 * epsilon) * epsilon; + isperrboundB = (5.0 + 72.0 * epsilon) * epsilon; + isperrboundC = (71.0 + 1408.0 * epsilon) * epsilon * epsilon; +} + +/** + * fast_expansion_sum_zeroelim() Sum two expansions, eliminating zero + * components from the output expansion. + * + * Sets h = e + f. See the long version of my paper for details. + * h cannot be e or f. + */ +static int fast_expansion_sum_zeroelim( + int elen, const double *e, int flen, const double *f, double *h) +{ + double Q; + INEXACT double Qnew; + INEXACT double hh; + INEXACT double bvirt; + double avirt, bround, around; + int eindex, findex, hindex; + double enow, fnow; + + enow = e[0]; + fnow = f[0]; + eindex = findex = 0; + if ((fnow > enow) == (fnow > -enow)) { + Q = enow; + enow = e[++eindex]; + } + else { + Q = fnow; + fnow = f[++findex]; + } + hindex = 0; + if ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Fast_Two_Sum(enow, Q, Qnew, hh); + enow = e[++eindex]; + } + else { + Fast_Two_Sum(fnow, Q, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + while ((eindex < elen) && (findex < flen)) { + if ((fnow > enow) == (fnow > -enow)) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + } + else { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + } + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + } + while (eindex < elen) { + Two_Sum(Q, enow, Qnew, hh); + enow = e[++eindex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + while (findex < flen) { + Two_Sum(Q, fnow, Qnew, hh); + fnow = f[++findex]; + Q = Qnew; + if (hh != 0.0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/* scale_expansion_zeroelim() Multiply an expansion by a scalar, + * eliminating zero components from the + * output expansion. + * + * Sets h = be. See either version of my paper for details. + * e and h cannot be the same. + */ +static int scale_expansion_zeroelim(int elen, const double *e, double b, double *h) +{ + INEXACT double Q, sum; + double hh; + INEXACT double product1; + double product0; + int eindex, hindex; + double enow; + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + + Split(b, bhi, blo); + Two_Product_Presplit(e[0], b, bhi, blo, Q, hh); + hindex = 0; + if (hh != 0) { + h[hindex++] = hh; + } + for (eindex = 1; eindex < elen; eindex++) { + enow = e[eindex]; + Two_Product_Presplit(enow, b, bhi, blo, product1, product0); + Two_Sum(Q, product0, sum, hh); + if (hh != 0) { + h[hindex++] = hh; + } + Fast_Two_Sum(product1, sum, Q, hh); + if (hh != 0) { + h[hindex++] = hh; + } + } + if ((Q != 0.0) || (hindex == 0)) { + h[hindex++] = Q; + } + return hindex; +} + +/* estimate() Produce a one-word estimate of an expansion's value. */ +static double estimate(int elen, const double *e) +{ + double Q; + int eindex; + + Q = e[0]; + for (eindex = 1; eindex < elen; eindex++) { + Q += e[eindex]; + } + return Q; +} + +/** + * orient2dfast() Approximate 2D orientation test. Non-robust. + * orient2d() Adaptive exact 2D orientation test. Robust. + * Return a positive value if the points pa, pb, and pc occur + * in counterclockwise order; a negative value if they occur + * in clockwise order; and zero if they are co-linear. The + * result is also a rough approximation of twice the signed + * area of the triangle defined by the three points. + * + * The second uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. In orient2d() only, + * this determinant is computed adaptively, in the sense that exact + * arithmetic is used only to the degree it is needed to ensure that the + * returned value has the correct sign. Hence, orient2d() is usually quite + * fast, but will run more slowly when the input points are co-linear or + * nearly so. + */ + +double orient2dfast(const double *pa, const double *pb, const double *pc) +{ + double acx, bcx, acy, bcy; + + acx = pa[0] - pc[0]; + bcx = pb[0] - pc[0]; + acy = pa[1] - pc[1]; + bcy = pb[1] - pc[1]; + return acx * bcy - acy * bcx; +} + +static double orient2dadapt(const double *pa, const double *pb, const double *pc, double detsum) +{ + INEXACT double acx, acy, bcx, bcy; + double acxtail, acytail, bcxtail, bcytail; + INEXACT double detleft, detright; + double detlefttail, detrighttail; + double det, errbound; + double B[4], C1[8], C2[12], D[16]; + INEXACT double B3; + int C1length, C2length, Dlength; + double u[4]; + INEXACT double u3; + INEXACT double s1, t1; + double s0, t0; + + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + INEXACT double _i, _j; + double _0; + + acx = (double)(pa[0] - pc[0]); + bcx = (double)(pb[0] - pc[0]); + acy = (double)(pa[1] - pc[1]); + bcy = (double)(pb[1] - pc[1]); + + Two_Product(acx, bcy, detleft, detlefttail); + Two_Product(acy, bcx, detright, detrighttail); + + Two_Two_Diff(detleft, detlefttail, detright, detrighttail, B3, B[2], B[1], B[0]); + B[3] = B3; + + det = estimate(4, B); + errbound = ccwerrboundB * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pc[0], acx, acxtail); + Two_Diff_Tail(pb[0], pc[0], bcx, bcxtail); + Two_Diff_Tail(pa[1], pc[1], acy, acytail); + Two_Diff_Tail(pb[1], pc[1], bcy, bcytail); + + if ((acxtail == 0.0) && (acytail == 0.0) && (bcxtail == 0.0) && (bcytail == 0.0)) { + return det; + } + + errbound = ccwerrboundC * detsum + resulterrbound * Absolute(det); + det += (acx * bcytail + bcy * acxtail) - (acy * bcxtail + bcx * acytail); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Product(acxtail, bcy, s1, s0); + Two_Product(acytail, bcx, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C1length = fast_expansion_sum_zeroelim(4, B, 4, u, C1); + + Two_Product(acx, bcytail, s1, s0); + Two_Product(acy, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + C2length = fast_expansion_sum_zeroelim(C1length, C1, 4, u, C2); + + Two_Product(acxtail, bcytail, s1, s0); + Two_Product(acytail, bcxtail, t1, t0); + Two_Two_Diff(s1, s0, t1, t0, u3, u[2], u[1], u[0]); + u[3] = u3; + Dlength = fast_expansion_sum_zeroelim(C2length, C2, 4, u, D); + + return (D[Dlength - 1]); +} + +double orient2d(const double *pa, const double *pb, const double *pc) +{ + double detleft, detright, det; + double detsum, errbound; + + detleft = (pa[0] - pc[0]) * (pb[1] - pc[1]); + detright = (pa[1] - pc[1]) * (pb[0] - pc[0]); + det = detleft - detright; + + if (detleft > 0.0) { + if (detright <= 0.0) { + return det; + } + detsum = detleft + detright; + } + else if (detleft < 0.0) { + if (detright >= 0.0) { + return det; + } + detsum = -detleft - detright; + } + else { + return det; + } + + errbound = ccwerrboundA * detsum; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return orient2dadapt(pa, pb, pc, detsum); +} + +/** + * orient3dfast() Approximate 3D orientation test. Nonrobust. + * orient3d() Adaptive exact 3D orientation test. Robust. + * + * Return a positive value if the point pd lies below the + * plane passing through pa, pb, and pc; "below" is defined so + * that pa, pb, and pc appear in counterclockwise order when + * viewed from above the plane. Returns a negative value if + * pd lies above the plane. Returns zero if the points are + * co-planar. The result is also a rough approximation of six + * times the signed volume of the tetrahedron defined by the + * four points. + * + * The second uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. In orient3d() only, + * this determinant is computed adaptively, in the sense that exact + * arithmetic is used only to the degree it is needed to ensure that the + * returned value has the correct sign. Hence, orient3d() is usually quite + * fast, but will run more slowly when the input points are co-planar or + * nearly so. + */ + +double orient3dfast(const double *pa, const double *pb, const double *pc, const double *pd) +{ + double adx, bdx, cdx; + double ady, bdy, cdy; + double adz, bdz, cdz; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + return adx * (bdy * cdz - bdz * cdy) + bdx * (cdy * adz - cdz * ady) + + cdx * (ady * bdz - adz * bdy); +} + +/** + * \note since this code comes from an external source, prefer not to break it + * up to fix this clang-tidy warning. + * NOLINTNEXTLINE: readability-function-size + */ +static double orient3dadapt( + const double *pa, const double *pb, const double *pc, const double *pd, double permanent) +{ + INEXACT double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + double det, errbound; + + INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + double bc[4], ca[4], ab[4]; + INEXACT double bc3, ca3, ab3; + double adet[8], bdet[8], cdet[8]; + int alen, blen, clen; + double abdet[16]; + int ablen; + double *finnow, *finother, *finswap; + double fin1[192], fin2[192]; + int finlength; + + double adxtail, bdxtail, cdxtail; + double adytail, bdytail, cdytail; + double adztail, bdztail, cdztail; + INEXACT double at_blarge, at_clarge; + INEXACT double bt_clarge, bt_alarge; + INEXACT double ct_alarge, ct_blarge; + double at_b[4], at_c[4], bt_c[4], bt_a[4], ct_a[4], ct_b[4]; + int at_blen, at_clen, bt_clen, bt_alen, ct_alen, ct_blen; + INEXACT double bdxt_cdy1, cdxt_bdy1, cdxt_ady1; + INEXACT double adxt_cdy1, adxt_bdy1, bdxt_ady1; + double bdxt_cdy0, cdxt_bdy0, cdxt_ady0; + double adxt_cdy0, adxt_bdy0, bdxt_ady0; + INEXACT double bdyt_cdx1, cdyt_bdx1, cdyt_adx1; + INEXACT double adyt_cdx1, adyt_bdx1, bdyt_adx1; + double bdyt_cdx0, cdyt_bdx0, cdyt_adx0; + double adyt_cdx0, adyt_bdx0, bdyt_adx0; + double bct[8], cat[8], abt[8]; + int bctlen, catlen, abtlen; + INEXACT double bdxt_cdyt1, cdxt_bdyt1, cdxt_adyt1; + INEXACT double adxt_cdyt1, adxt_bdyt1, bdxt_adyt1; + double bdxt_cdyt0, cdxt_bdyt0, cdxt_adyt0; + double adxt_cdyt0, adxt_bdyt0, bdxt_adyt0; + double u[4], v[12], w[16]; + INEXACT double u3; + int vlength, wlength; + double negate; + + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + INEXACT double _i, _j, _k; + double _0; + + adx = (double)(pa[0] - pd[0]); + bdx = (double)(pb[0] - pd[0]); + cdx = (double)(pc[0] - pd[0]); + ady = (double)(pa[1] - pd[1]); + bdy = (double)(pb[1] - pd[1]); + cdy = (double)(pc[1] - pd[1]); + adz = (double)(pa[2] - pd[2]); + bdz = (double)(pb[2] - pd[2]); + cdz = (double)(pc[2] - pd[2]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + alen = scale_expansion_zeroelim(4, bc, adz, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + blen = scale_expansion_zeroelim(4, ca, bdz, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + clen = scale_expansion_zeroelim(4, ab, cdz, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = o3derrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + Two_Diff_Tail(pa[2], pd[2], adz, adztail); + Two_Diff_Tail(pb[2], pd[2], bdz, bdztail); + Two_Diff_Tail(pc[2], pd[2], cdz, cdztail); + + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) && + (bdytail == 0.0) && (cdytail == 0.0) && (adztail == 0.0) && (bdztail == 0.0) && + (cdztail == 0.0)) { + return det; + } + + errbound = o3derrboundC * permanent + resulterrbound * Absolute(det); + det += (adz * ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + adztail * (bdx * cdy - bdy * cdx)) + + (bdz * ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + bdztail * (cdx * ady - cdy * adx)) + + (cdz * ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + cdztail * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if (adxtail == 0.0) { + if (adytail == 0.0) { + at_b[0] = 0.0; + at_blen = 1; + at_c[0] = 0.0; + at_clen = 1; + } + else { + negate = -adytail; + Two_Product(negate, bdx, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + Two_Product(adytail, cdx, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + } + else { + if (adytail == 0.0) { + Two_Product(adxtail, bdy, at_blarge, at_b[0]); + at_b[1] = at_blarge; + at_blen = 2; + negate = -adxtail; + Two_Product(negate, cdy, at_clarge, at_c[0]); + at_c[1] = at_clarge; + at_clen = 2; + } + else { + Two_Product(adxtail, bdy, adxt_bdy1, adxt_bdy0); + Two_Product(adytail, bdx, adyt_bdx1, adyt_bdx0); + Two_Two_Diff( + adxt_bdy1, adxt_bdy0, adyt_bdx1, adyt_bdx0, at_blarge, at_b[2], at_b[1], at_b[0]); + at_b[3] = at_blarge; + at_blen = 4; + Two_Product(adytail, cdx, adyt_cdx1, adyt_cdx0); + Two_Product(adxtail, cdy, adxt_cdy1, adxt_cdy0); + Two_Two_Diff( + adyt_cdx1, adyt_cdx0, adxt_cdy1, adxt_cdy0, at_clarge, at_c[2], at_c[1], at_c[0]); + at_c[3] = at_clarge; + at_clen = 4; + } + } + if (bdxtail == 0.0) { + if (bdytail == 0.0) { + bt_c[0] = 0.0; + bt_clen = 1; + bt_a[0] = 0.0; + bt_alen = 1; + } + else { + negate = -bdytail; + Two_Product(negate, cdx, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + Two_Product(bdytail, adx, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + } + else { + if (bdytail == 0.0) { + Two_Product(bdxtail, cdy, bt_clarge, bt_c[0]); + bt_c[1] = bt_clarge; + bt_clen = 2; + negate = -bdxtail; + Two_Product(negate, ady, bt_alarge, bt_a[0]); + bt_a[1] = bt_alarge; + bt_alen = 2; + } + else { + Two_Product(bdxtail, cdy, bdxt_cdy1, bdxt_cdy0); + Two_Product(bdytail, cdx, bdyt_cdx1, bdyt_cdx0); + Two_Two_Diff( + bdxt_cdy1, bdxt_cdy0, bdyt_cdx1, bdyt_cdx0, bt_clarge, bt_c[2], bt_c[1], bt_c[0]); + bt_c[3] = bt_clarge; + bt_clen = 4; + Two_Product(bdytail, adx, bdyt_adx1, bdyt_adx0); + Two_Product(bdxtail, ady, bdxt_ady1, bdxt_ady0); + Two_Two_Diff( + bdyt_adx1, bdyt_adx0, bdxt_ady1, bdxt_ady0, bt_alarge, bt_a[2], bt_a[1], bt_a[0]); + bt_a[3] = bt_alarge; + bt_alen = 4; + } + } + if (cdxtail == 0.0) { + if (cdytail == 0.0) { + ct_a[0] = 0.0; + ct_alen = 1; + ct_b[0] = 0.0; + ct_blen = 1; + } + else { + negate = -cdytail; + Two_Product(negate, adx, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + Two_Product(cdytail, bdx, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + } + else { + if (cdytail == 0.0) { + Two_Product(cdxtail, ady, ct_alarge, ct_a[0]); + ct_a[1] = ct_alarge; + ct_alen = 2; + negate = -cdxtail; + Two_Product(negate, bdy, ct_blarge, ct_b[0]); + ct_b[1] = ct_blarge; + ct_blen = 2; + } + else { + Two_Product(cdxtail, ady, cdxt_ady1, cdxt_ady0); + Two_Product(cdytail, adx, cdyt_adx1, cdyt_adx0); + Two_Two_Diff( + cdxt_ady1, cdxt_ady0, cdyt_adx1, cdyt_adx0, ct_alarge, ct_a[2], ct_a[1], ct_a[0]); + ct_a[3] = ct_alarge; + ct_alen = 4; + Two_Product(cdytail, bdx, cdyt_bdx1, cdyt_bdx0); + Two_Product(cdxtail, bdy, cdxt_bdy1, cdxt_bdy0); + Two_Two_Diff( + cdyt_bdx1, cdyt_bdx0, cdxt_bdy1, cdxt_bdy0, ct_blarge, ct_b[2], ct_b[1], ct_b[0]); + ct_b[3] = ct_blarge; + ct_blen = 4; + } + } + + bctlen = fast_expansion_sum_zeroelim(bt_clen, bt_c, ct_blen, ct_b, bct); + wlength = scale_expansion_zeroelim(bctlen, bct, adz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + catlen = fast_expansion_sum_zeroelim(ct_alen, ct_a, at_clen, at_c, cat); + wlength = scale_expansion_zeroelim(catlen, cat, bdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + abtlen = fast_expansion_sum_zeroelim(at_blen, at_b, bt_alen, bt_a, abt); + wlength = scale_expansion_zeroelim(abtlen, abt, cdz, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + if (adztail != 0.0) { + vlength = scale_expansion_zeroelim(4, bc, adztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ca, bdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdztail != 0.0) { + vlength = scale_expansion_zeroelim(4, ab, cdztail, v); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, vlength, v, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + if (adxtail != 0.0) { + if (bdytail != 0.0) { + Two_Product(adxtail, bdytail, adxt_bdyt1, adxt_bdyt0); + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(adxt_bdyt1, adxt_bdyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (cdytail != 0.0) { + negate = -adxtail; + Two_Product(negate, cdytail, adxt_cdyt1, adxt_cdyt0); + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(adxt_cdyt1, adxt_cdyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + if (bdxtail != 0.0) { + if (cdytail != 0.0) { + Two_Product(bdxtail, cdytail, bdxt_cdyt1, bdxt_cdyt0); + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adztail != 0.0) { + Two_One_Product(bdxt_cdyt1, bdxt_cdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (adytail != 0.0) { + negate = -bdxtail; + Two_Product(negate, adytail, bdxt_adyt1, bdxt_adyt0); + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdztail != 0.0) { + Two_One_Product(bdxt_adyt1, bdxt_adyt0, cdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + if (cdxtail != 0.0) { + if (adytail != 0.0) { + Two_Product(cdxtail, adytail, cdxt_adyt1, cdxt_adyt0); + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdztail != 0.0) { + Two_One_Product(cdxt_adyt1, cdxt_adyt0, bdztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if (bdytail != 0.0) { + negate = -cdxtail; + Two_Product(negate, bdytail, cdxt_bdyt1, cdxt_bdyt0); + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adz, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adztail != 0.0) { + Two_One_Product(cdxt_bdyt1, cdxt_bdyt0, adztail, u3, u[2], u[1], u[0]); + u[3] = u3; + finlength = fast_expansion_sum_zeroelim(finlength, finnow, 4, u, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + } + + if (adztail != 0.0) { + wlength = scale_expansion_zeroelim(bctlen, bct, adztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdztail != 0.0) { + wlength = scale_expansion_zeroelim(catlen, cat, bdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdztail != 0.0) { + wlength = scale_expansion_zeroelim(abtlen, abt, cdztail, w); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, wlength, w, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + return finnow[finlength - 1]; +} + +double orient3d(const double *pa, const double *pb, const double *pc, const double *pd) +{ + double adx, bdx, cdx, ady, bdy, cdy, adz, bdz, cdz; + double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + double det; + double permanent, errbound; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + adz = pa[2] - pd[2]; + bdz = pb[2] - pd[2]; + cdz = pc[2] - pd[2]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + + det = adz * (bdxcdy - cdxbdy) + bdz * (cdxady - adxcdy) + cdz * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * Absolute(adz) + + (Absolute(cdxady) + Absolute(adxcdy)) * Absolute(bdz) + + (Absolute(adxbdy) + Absolute(bdxady)) * Absolute(cdz); + errbound = o3derrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return orient3dadapt(pa, pb, pc, pd, permanent); +} + +/** + * incirclefast() Approximate 2D incircle test. Non-robust. + * incircle() + * + * Return a positive value if the point pd lies inside the + * circle passing through pa, pb, and pc; a negative value if + * it lies outside; and zero if the four points are co-circular. + * The points pa, pb, and pc must be in counterclockwise + * order, or the sign of the result will be reversed. + * + * The second uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. In incircle() only, + * this determinant is computed adaptively, in the sense that exact + * arithmetic is used only to the degree it is needed to ensure that the + * returned value has the correct sign. Hence, incircle() is usually quite + * fast, but will run more slowly when the input points are co-circular or + * nearly so. + */ + +double incirclefast(const double *pa, const double *pb, const double *pc, const double *pd) +{ + double adx, ady, bdx, bdy, cdx, cdy; + double abdet, bcdet, cadet; + double alift, blift, clift; + + adx = pa[0] - pd[0]; + ady = pa[1] - pd[1]; + bdx = pb[0] - pd[0]; + bdy = pb[1] - pd[1]; + cdx = pc[0] - pd[0]; + cdy = pc[1] - pd[1]; + + abdet = adx * bdy - bdx * ady; + bcdet = bdx * cdy - cdx * bdy; + cadet = cdx * ady - adx * cdy; + alift = adx * adx + ady * ady; + blift = bdx * bdx + bdy * bdy; + clift = cdx * cdx + cdy * cdy; + + return alift * bcdet + blift * cadet + clift * abdet; +} + +/** + * \note since this code comes from an external source, prefer not to break it + * up to fix this clang-tidy warning. + * NOLINTNEXTLINE: readability-function-size + */ +static double incircleadapt( + const double *pa, const double *pb, const double *pc, const double *pd, double permanent) +{ + INEXACT double adx, bdx, cdx, ady, bdy, cdy; + double det, errbound; + + INEXACT double bdxcdy1, cdxbdy1, cdxady1, adxcdy1, adxbdy1, bdxady1; + double bdxcdy0, cdxbdy0, cdxady0, adxcdy0, adxbdy0, bdxady0; + double bc[4], ca[4], ab[4]; + INEXACT double bc3, ca3, ab3; + double axbc[8], axxbc[16], aybc[8], ayybc[16], adet[32]; + int axbclen, axxbclen, aybclen, ayybclen, alen; + double bxca[8], bxxca[16], byca[8], byyca[16], bdet[32]; + int bxcalen, bxxcalen, bycalen, byycalen, blen; + double cxab[8], cxxab[16], cyab[8], cyyab[16], cdet[32]; + int cxablen, cxxablen, cyablen, cyyablen, clen; + double abdet[64]; + int ablen; + double fin1[1152], fin2[1152]; + double *finnow, *finother, *finswap; + int finlength; + + double adxtail, bdxtail, cdxtail, adytail, bdytail, cdytail; + INEXACT double adxadx1, adyady1, bdxbdx1, bdybdy1, cdxcdx1, cdycdy1; + double adxadx0, adyady0, bdxbdx0, bdybdy0, cdxcdx0, cdycdy0; + double aa[4], bb[4], cc[4]; + INEXACT double aa3, bb3, cc3; + INEXACT double ti1, tj1; + double ti0, tj0; + double u[4], v[4]; + INEXACT double u3, v3; + double temp8[8], temp16a[16], temp16b[16], temp16c[16]; + double temp32a[32], temp32b[32], temp48[48], temp64[64]; + int temp8len, temp16alen, temp16blen, temp16clen; + int temp32alen, temp32blen, temp48len, temp64len; + double axtbb[8], axtcc[8], aytbb[8], aytcc[8]; + int axtbblen, axtcclen, aytbblen, aytcclen; + double bxtaa[8], bxtcc[8], bytaa[8], bytcc[8]; + int bxtaalen, bxtcclen, bytaalen, bytcclen; + double cxtaa[8], cxtbb[8], cytaa[8], cytbb[8]; + int cxtaalen, cxtbblen, cytaalen, cytbblen; + double axtbc[8], aytbc[8], bxtca[8], bytca[8], cxtab[8], cytab[8]; + int axtbclen, aytbclen, bxtcalen, bytcalen, cxtablen, cytablen; + double axtbct[16], aytbct[16], bxtcat[16], bytcat[16], cxtabt[16], cytabt[16]; + int axtbctlen, aytbctlen, bxtcatlen, bytcatlen, cxtabtlen, cytabtlen; + double axtbctt[8], aytbctt[8], bxtcatt[8]; + double bytcatt[8], cxtabtt[8], cytabtt[8]; + int axtbcttlen, aytbcttlen, bxtcattlen, bytcattlen, cxtabttlen, cytabttlen; + double abt[8], bct[8], cat[8]; + int abtlen, bctlen, catlen; + double abtt[4], bctt[4], catt[4]; + int abttlen, bcttlen, cattlen; + INEXACT double abtt3, bctt3, catt3; + double negate; + + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + INEXACT double _i, _j; + double _0; + + adx = (double)(pa[0] - pd[0]); + bdx = (double)(pb[0] - pd[0]); + cdx = (double)(pc[0] - pd[0]); + ady = (double)(pa[1] - pd[1]); + bdy = (double)(pb[1] - pd[1]); + cdy = (double)(pc[1] - pd[1]); + + Two_Product(bdx, cdy, bdxcdy1, bdxcdy0); + Two_Product(cdx, bdy, cdxbdy1, cdxbdy0); + Two_Two_Diff(bdxcdy1, bdxcdy0, cdxbdy1, cdxbdy0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + axbclen = scale_expansion_zeroelim(4, bc, adx, axbc); + axxbclen = scale_expansion_zeroelim(axbclen, axbc, adx, axxbc); + aybclen = scale_expansion_zeroelim(4, bc, ady, aybc); + ayybclen = scale_expansion_zeroelim(aybclen, aybc, ady, ayybc); + alen = fast_expansion_sum_zeroelim(axxbclen, axxbc, ayybclen, ayybc, adet); + + Two_Product(cdx, ady, cdxady1, cdxady0); + Two_Product(adx, cdy, adxcdy1, adxcdy0); + Two_Two_Diff(cdxady1, cdxady0, adxcdy1, adxcdy0, ca3, ca[2], ca[1], ca[0]); + ca[3] = ca3; + bxcalen = scale_expansion_zeroelim(4, ca, bdx, bxca); + bxxcalen = scale_expansion_zeroelim(bxcalen, bxca, bdx, bxxca); + bycalen = scale_expansion_zeroelim(4, ca, bdy, byca); + byycalen = scale_expansion_zeroelim(bycalen, byca, bdy, byyca); + blen = fast_expansion_sum_zeroelim(bxxcalen, bxxca, byycalen, byyca, bdet); + + Two_Product(adx, bdy, adxbdy1, adxbdy0); + Two_Product(bdx, ady, bdxady1, bdxady0); + Two_Two_Diff(adxbdy1, adxbdy0, bdxady1, bdxady0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + cxablen = scale_expansion_zeroelim(4, ab, cdx, cxab); + cxxablen = scale_expansion_zeroelim(cxablen, cxab, cdx, cxxab); + cyablen = scale_expansion_zeroelim(4, ab, cdy, cyab); + cyyablen = scale_expansion_zeroelim(cyablen, cyab, cdy, cyyab); + clen = fast_expansion_sum_zeroelim(cxxablen, cxxab, cyyablen, cyyab, cdet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, clen, cdet, fin1); + + det = estimate(finlength, fin1); + errbound = iccerrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pd[0], adx, adxtail); + Two_Diff_Tail(pa[1], pd[1], ady, adytail); + Two_Diff_Tail(pb[0], pd[0], bdx, bdxtail); + Two_Diff_Tail(pb[1], pd[1], bdy, bdytail); + Two_Diff_Tail(pc[0], pd[0], cdx, cdxtail); + Two_Diff_Tail(pc[1], pd[1], cdy, cdytail); + if ((adxtail == 0.0) && (bdxtail == 0.0) && (cdxtail == 0.0) && (adytail == 0.0) && + (bdytail == 0.0) && (cdytail == 0.0)) { + return det; + } + + errbound = iccerrboundC * permanent + resulterrbound * Absolute(det); + det += ((adx * adx + ady * ady) * + ((bdx * cdytail + cdy * bdxtail) - (bdy * cdxtail + cdx * bdytail)) + + 2.0 * (adx * adxtail + ady * adytail) * (bdx * cdy - bdy * cdx)) + + ((bdx * bdx + bdy * bdy) * + ((cdx * adytail + ady * cdxtail) - (cdy * adxtail + adx * cdytail)) + + 2.0 * (bdx * bdxtail + bdy * bdytail) * (cdx * ady - cdy * adx)) + + ((cdx * cdx + cdy * cdy) * + ((adx * bdytail + bdy * adxtail) - (ady * bdxtail + bdx * adytail)) + + 2.0 * (cdx * cdxtail + cdy * cdytail) * (adx * bdy - ady * bdx)); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + finnow = fin1; + finother = fin2; + + if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { + Square(adx, adxadx1, adxadx0); + Square(ady, adyady1, adyady0); + Two_Two_Sum(adxadx1, adxadx0, adyady1, adyady0, aa3, aa[2], aa[1], aa[0]); + aa[3] = aa3; + } + if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { + Square(bdx, bdxbdx1, bdxbdx0); + Square(bdy, bdybdy1, bdybdy0); + Two_Two_Sum(bdxbdx1, bdxbdx0, bdybdy1, bdybdy0, bb3, bb[2], bb[1], bb[0]); + bb[3] = bb3; + } + if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { + Square(cdx, cdxcdx1, cdxcdx0); + Square(cdy, cdycdy1, cdycdy0); + Two_Two_Sum(cdxcdx1, cdxcdx0, cdycdy1, cdycdy0, cc3, cc[2], cc[1], cc[0]); + cc[3] = cc3; + } + + if (adxtail != 0.0) { + axtbclen = scale_expansion_zeroelim(4, bc, adxtail, axtbc); + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, 2.0 * adx, temp16a); + + axtcclen = scale_expansion_zeroelim(4, cc, adxtail, axtcc); + temp16blen = scale_expansion_zeroelim(axtcclen, axtcc, bdy, temp16b); + + axtbblen = scale_expansion_zeroelim(4, bb, adxtail, axtbb); + temp16clen = scale_expansion_zeroelim(axtbblen, axtbb, -cdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + aytbclen = scale_expansion_zeroelim(4, bc, adytail, aytbc); + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, 2.0 * ady, temp16a); + + aytbblen = scale_expansion_zeroelim(4, bb, adytail, aytbb); + temp16blen = scale_expansion_zeroelim(aytbblen, aytbb, cdx, temp16b); + + aytcclen = scale_expansion_zeroelim(4, cc, adytail, aytcc); + temp16clen = scale_expansion_zeroelim(aytcclen, aytcc, -bdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdxtail != 0.0) { + bxtcalen = scale_expansion_zeroelim(4, ca, bdxtail, bxtca); + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, 2.0 * bdx, temp16a); + + bxtaalen = scale_expansion_zeroelim(4, aa, bdxtail, bxtaa); + temp16blen = scale_expansion_zeroelim(bxtaalen, bxtaa, cdy, temp16b); + + bxtcclen = scale_expansion_zeroelim(4, cc, bdxtail, bxtcc); + temp16clen = scale_expansion_zeroelim(bxtcclen, bxtcc, -ady, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + bytcalen = scale_expansion_zeroelim(4, ca, bdytail, bytca); + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, 2.0 * bdy, temp16a); + + bytcclen = scale_expansion_zeroelim(4, cc, bdytail, bytcc); + temp16blen = scale_expansion_zeroelim(bytcclen, bytcc, adx, temp16b); + + bytaalen = scale_expansion_zeroelim(4, aa, bdytail, bytaa); + temp16clen = scale_expansion_zeroelim(bytaalen, bytaa, -cdx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdxtail != 0.0) { + cxtablen = scale_expansion_zeroelim(4, ab, cdxtail, cxtab); + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, 2.0 * cdx, temp16a); + + cxtbblen = scale_expansion_zeroelim(4, bb, cdxtail, cxtbb); + temp16blen = scale_expansion_zeroelim(cxtbblen, cxtbb, ady, temp16b); + + cxtaalen = scale_expansion_zeroelim(4, aa, cdxtail, cxtaa); + temp16clen = scale_expansion_zeroelim(cxtaalen, cxtaa, -bdy, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + cytablen = scale_expansion_zeroelim(4, ab, cdytail, cytab); + temp16alen = scale_expansion_zeroelim(cytablen, cytab, 2.0 * cdy, temp16a); + + cytaalen = scale_expansion_zeroelim(4, aa, cdytail, cytaa); + temp16blen = scale_expansion_zeroelim(cytaalen, cytaa, bdx, temp16b); + + cytbblen = scale_expansion_zeroelim(4, bb, cdytail, cytbb); + temp16clen = scale_expansion_zeroelim(cytbblen, cytbb, -adx, temp16c); + + temp32alen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16clen, temp16c, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + if ((adxtail != 0.0) || (adytail != 0.0)) { + if ((bdxtail != 0.0) || (bdytail != 0.0) || (cdxtail != 0.0) || (cdytail != 0.0)) { + Two_Product(bdxtail, cdy, ti1, ti0); + Two_Product(bdx, cdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -bdy; + Two_Product(cdxtail, negate, ti1, ti0); + negate = -bdytail; + Two_Product(cdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + bctlen = fast_expansion_sum_zeroelim(4, u, 4, v, bct); + + Two_Product(bdxtail, cdytail, ti1, ti0); + Two_Product(cdxtail, bdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, bctt3, bctt[2], bctt[1], bctt[0]); + bctt[3] = bctt3; + bcttlen = 4; + } + else { + bct[0] = 0.0; + bctlen = 1; + bctt[0] = 0.0; + bcttlen = 1; + } + + if (adxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(axtbclen, axtbc, adxtail, temp16a); + axtbctlen = scale_expansion_zeroelim(bctlen, bct, adxtail, axtbct); + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, 2.0 * adx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, -adxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(axtbctlen, axtbct, adxtail, temp32a); + axtbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adxtail, axtbctt); + temp16alen = scale_expansion_zeroelim(axtbcttlen, axtbctt, 2.0 * adx, temp16a); + temp16blen = scale_expansion_zeroelim(axtbcttlen, axtbctt, adxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + temp16alen = scale_expansion_zeroelim(aytbclen, aytbc, adytail, temp16a); + aytbctlen = scale_expansion_zeroelim(bctlen, bct, adytail, aytbct); + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, 2.0 * ady, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(aytbctlen, aytbct, adytail, temp32a); + aytbcttlen = scale_expansion_zeroelim(bcttlen, bctt, adytail, aytbctt); + temp16alen = scale_expansion_zeroelim(aytbcttlen, aytbctt, 2.0 * ady, temp16a); + temp16blen = scale_expansion_zeroelim(aytbcttlen, aytbctt, adytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if ((bdxtail != 0.0) || (bdytail != 0.0)) { + if ((cdxtail != 0.0) || (cdytail != 0.0) || (adxtail != 0.0) || (adytail != 0.0)) { + Two_Product(cdxtail, ady, ti1, ti0); + Two_Product(cdx, adytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -cdy; + Two_Product(adxtail, negate, ti1, ti0); + negate = -cdytail; + Two_Product(adx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + catlen = fast_expansion_sum_zeroelim(4, u, 4, v, cat); + + Two_Product(cdxtail, adytail, ti1, ti0); + Two_Product(adxtail, cdytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, catt3, catt[2], catt[1], catt[0]); + catt[3] = catt3; + cattlen = 4; + } + else { + cat[0] = 0.0; + catlen = 1; + catt[0] = 0.0; + cattlen = 1; + } + + if (bdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(bxtcalen, bxtca, bdxtail, temp16a); + bxtcatlen = scale_expansion_zeroelim(catlen, cat, bdxtail, bxtcat); + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, 2.0 * bdx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (cdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, cdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, cc, -bdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(bxtcatlen, bxtcat, bdxtail, temp32a); + bxtcattlen = scale_expansion_zeroelim(cattlen, catt, bdxtail, bxtcatt); + temp16alen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, 2.0 * bdx, temp16a); + temp16blen = scale_expansion_zeroelim(bxtcattlen, bxtcatt, bdxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(bytcalen, bytca, bdytail, temp16a); + bytcatlen = scale_expansion_zeroelim(catlen, cat, bdytail, bytcat); + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, 2.0 * bdy, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(bytcatlen, bytcat, bdytail, temp32a); + bytcattlen = scale_expansion_zeroelim(cattlen, catt, bdytail, bytcatt); + temp16alen = scale_expansion_zeroelim(bytcattlen, bytcatt, 2.0 * bdy, temp16a); + temp16blen = scale_expansion_zeroelim(bytcattlen, bytcatt, bdytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + if ((cdxtail != 0.0) || (cdytail != 0.0)) { + if ((adxtail != 0.0) || (adytail != 0.0) || (bdxtail != 0.0) || (bdytail != 0.0)) { + Two_Product(adxtail, bdy, ti1, ti0); + Two_Product(adx, bdytail, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, u3, u[2], u[1], u[0]); + u[3] = u3; + negate = -ady; + Two_Product(bdxtail, negate, ti1, ti0); + negate = -adytail; + Two_Product(bdx, negate, tj1, tj0); + Two_Two_Sum(ti1, ti0, tj1, tj0, v3, v[2], v[1], v[0]); + v[3] = v3; + abtlen = fast_expansion_sum_zeroelim(4, u, 4, v, abt); + + Two_Product(adxtail, bdytail, ti1, ti0); + Two_Product(bdxtail, adytail, tj1, tj0); + Two_Two_Diff(ti1, ti0, tj1, tj0, abtt3, abtt[2], abtt[1], abtt[0]); + abtt[3] = abtt3; + abttlen = 4; + } + else { + abt[0] = 0.0; + abtlen = 1; + abtt[0] = 0.0; + abttlen = 1; + } + + if (cdxtail != 0.0) { + temp16alen = scale_expansion_zeroelim(cxtablen, cxtab, cdxtail, temp16a); + cxtabtlen = scale_expansion_zeroelim(abtlen, abt, cdxtail, cxtabt); + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, 2.0 * cdx, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + if (adytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, bb, cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, adytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (bdytail != 0.0) { + temp8len = scale_expansion_zeroelim(4, aa, -cdxtail, temp8); + temp16alen = scale_expansion_zeroelim(temp8len, temp8, bdytail, temp16a); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp16alen, temp16a, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + + temp32alen = scale_expansion_zeroelim(cxtabtlen, cxtabt, cdxtail, temp32a); + cxtabttlen = scale_expansion_zeroelim(abttlen, abtt, cdxtail, cxtabtt); + temp16alen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, 2.0 * cdx, temp16a); + temp16blen = scale_expansion_zeroelim(cxtabttlen, cxtabtt, cdxtail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + if (cdytail != 0.0) { + temp16alen = scale_expansion_zeroelim(cytablen, cytab, cdytail, temp16a); + cytabtlen = scale_expansion_zeroelim(abtlen, abt, cdytail, cytabt); + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, 2.0 * cdy, temp32a); + temp48len = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp32alen, temp32a, temp48); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp48len, temp48, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + + temp32alen = scale_expansion_zeroelim(cytabtlen, cytabt, cdytail, temp32a); + cytabttlen = scale_expansion_zeroelim(abttlen, abtt, cdytail, cytabtt); + temp16alen = scale_expansion_zeroelim(cytabttlen, cytabtt, 2.0 * cdy, temp16a); + temp16blen = scale_expansion_zeroelim(cytabttlen, cytabtt, cdytail, temp16b); + temp32blen = fast_expansion_sum_zeroelim(temp16alen, temp16a, temp16blen, temp16b, temp32b); + temp64len = fast_expansion_sum_zeroelim(temp32alen, temp32a, temp32blen, temp32b, temp64); + finlength = fast_expansion_sum_zeroelim(finlength, finnow, temp64len, temp64, finother); + finswap = finnow; + finnow = finother; + finother = finswap; + } + } + + return finnow[finlength - 1]; +} + +double incircle(const double *pa, const double *pb, const double *pc, const double *pd) +{ + double adx, bdx, cdx, ady, bdy, cdy; + double bdxcdy, cdxbdy, cdxady, adxcdy, adxbdy, bdxady; + double alift, blift, clift; + double det; + double permanent, errbound; + + adx = pa[0] - pd[0]; + bdx = pb[0] - pd[0]; + cdx = pc[0] - pd[0]; + ady = pa[1] - pd[1]; + bdy = pb[1] - pd[1]; + cdy = pc[1] - pd[1]; + + bdxcdy = bdx * cdy; + cdxbdy = cdx * bdy; + alift = adx * adx + ady * ady; + + cdxady = cdx * ady; + adxcdy = adx * cdy; + blift = bdx * bdx + bdy * bdy; + + adxbdy = adx * bdy; + bdxady = bdx * ady; + clift = cdx * cdx + cdy * cdy; + + det = alift * (bdxcdy - cdxbdy) + blift * (cdxady - adxcdy) + clift * (adxbdy - bdxady); + + permanent = (Absolute(bdxcdy) + Absolute(cdxbdy)) * alift + + (Absolute(cdxady) + Absolute(adxcdy)) * blift + + (Absolute(adxbdy) + Absolute(bdxady)) * clift; + errbound = iccerrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return incircleadapt(pa, pb, pc, pd, permanent); +} + +/** + * inspherefast() Approximate 3D insphere test. Non-robust. + * insphere() Adaptive exact 3D insphere test. Robust. + * + * Return a positive value if the point pe lies inside the + * sphere passing through pa, pb, pc, and pd; a negative value + * if it lies outside; and zero if the five points are + * co-spherical. The points pa, pb, pc, and pd must be ordered + * so that they have a positive orientation (as defined by + * orient3d()), or the sign of the result will be reversed. + * + * The second uses exact arithmetic to ensure a correct answer. The + * result returned is the determinant of a matrix. In insphere() only, + * this determinant is computed adaptively, in the sense that exact + * arithmetic is used only to the degree it is needed to ensure that the + * returned value has the correct sign. Hence, insphere() is usually quite + * fast, but will run more slowly when the input points are co-spherical or + * nearly so. + */ + +double inspherefast( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe) +{ + double aex, bex, cex, dex; + double aey, bey, cey, dey; + double aez, bez, cez, dez; + double alift, blift, clift, dlift; + double ab, bc, cd, da, ac, bd; + double abc, bcd, cda, dab; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + ab = aex * bey - bex * aey; + bc = bex * cey - cex * bey; + cd = cex * dey - dex * cey; + da = dex * aey - aex * dey; + + ac = aex * cey - cex * aey; + bd = bex * dey - dex * bey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + return (dlift * abc - clift * dab) + (blift * cda - alift * bcd); +} + +static double insphereexact( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe) +{ + INEXACT double axby1, bxcy1, cxdy1, dxey1, exay1; + INEXACT double bxay1, cxby1, dxcy1, exdy1, axey1; + INEXACT double axcy1, bxdy1, cxey1, dxay1, exby1; + INEXACT double cxay1, dxby1, excy1, axdy1, bxey1; + double axby0, bxcy0, cxdy0, dxey0, exay0; + double bxay0, cxby0, dxcy0, exdy0, axey0; + double axcy0, bxdy0, cxey0, dxay0, exby0; + double cxay0, dxby0, excy0, axdy0, bxey0; + double ab[4], bc[4], cd[4], de[4], ea[4]; + double ac[4], bd[4], ce[4], da[4], eb[4]; + double temp8a[8], temp8b[8], temp16[16]; + int temp8alen, temp8blen, temp16len; + double abc[24], bcd[24], cde[24], dea[24], eab[24]; + double abd[24], bce[24], cda[24], deb[24], eac[24]; + int abclen, bcdlen, cdelen, dealen, eablen; + int abdlen, bcelen, cdalen, deblen, eaclen; + double temp48a[48], temp48b[48]; + int temp48alen, temp48blen; + double abcd[96], bcde[96], cdea[96], deab[96], eabc[96]; + int abcdlen, bcdelen, cdealen, deablen, eabclen; + double temp192[192]; + double det384x[384], det384y[384], det384z[384]; + int xlen, ylen, zlen; + double detxy[768]; + int xylen; + double adet[1152], bdet[1152], cdet[1152], ddet[1152], edet[1152]; + int alen, blen, clen, dlen, elen; + double abdet[2304], cddet[2304], cdedet[3456]; + int ablen, cdlen; + double deter[5760]; + int deterlen; + int i; + + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + INEXACT double _i, _j; + double _0; + + Two_Product(pa[0], pb[1], axby1, axby0); + Two_Product(pb[0], pa[1], bxay1, bxay0); + Two_Two_Diff(axby1, axby0, bxay1, bxay0, ab[3], ab[2], ab[1], ab[0]); + + Two_Product(pb[0], pc[1], bxcy1, bxcy0); + Two_Product(pc[0], pb[1], cxby1, cxby0); + Two_Two_Diff(bxcy1, bxcy0, cxby1, cxby0, bc[3], bc[2], bc[1], bc[0]); + + Two_Product(pc[0], pd[1], cxdy1, cxdy0); + Two_Product(pd[0], pc[1], dxcy1, dxcy0); + Two_Two_Diff(cxdy1, cxdy0, dxcy1, dxcy0, cd[3], cd[2], cd[1], cd[0]); + + Two_Product(pd[0], pe[1], dxey1, dxey0); + Two_Product(pe[0], pd[1], exdy1, exdy0); + Two_Two_Diff(dxey1, dxey0, exdy1, exdy0, de[3], de[2], de[1], de[0]); + + Two_Product(pe[0], pa[1], exay1, exay0); + Two_Product(pa[0], pe[1], axey1, axey0); + Two_Two_Diff(exay1, exay0, axey1, axey0, ea[3], ea[2], ea[1], ea[0]); + + Two_Product(pa[0], pc[1], axcy1, axcy0); + Two_Product(pc[0], pa[1], cxay1, cxay0); + Two_Two_Diff(axcy1, axcy0, cxay1, cxay0, ac[3], ac[2], ac[1], ac[0]); + + Two_Product(pb[0], pd[1], bxdy1, bxdy0); + Two_Product(pd[0], pb[1], dxby1, dxby0); + Two_Two_Diff(bxdy1, bxdy0, dxby1, dxby0, bd[3], bd[2], bd[1], bd[0]); + + Two_Product(pc[0], pe[1], cxey1, cxey0); + Two_Product(pe[0], pc[1], excy1, excy0); + Two_Two_Diff(cxey1, cxey0, excy1, excy0, ce[3], ce[2], ce[1], ce[0]); + + Two_Product(pd[0], pa[1], dxay1, dxay0); + Two_Product(pa[0], pd[1], axdy1, axdy0); + Two_Two_Diff(dxay1, dxay0, axdy1, axdy0, da[3], da[2], da[1], da[0]); + + Two_Product(pe[0], pb[1], exby1, exby0); + Two_Product(pb[0], pe[1], bxey1, bxey0); + Two_Two_Diff(exby1, exby0, bxey1, bxey0, eb[3], eb[2], eb[1], eb[0]); + + temp8alen = scale_expansion_zeroelim(4, bc, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pc[2], temp8a); + abclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, abc); + + temp8alen = scale_expansion_zeroelim(4, cd, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pd[2], temp8a); + bcdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, bcd); + + temp8alen = scale_expansion_zeroelim(4, de, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, -pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pe[2], temp8a); + cdelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, cde); + + temp8alen = scale_expansion_zeroelim(4, ea, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, -pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, de, pa[2], temp8a); + dealen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, dea); + + temp8alen = scale_expansion_zeroelim(4, ab, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, -pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pb[2], temp8a); + eablen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, eab); + + temp8alen = scale_expansion_zeroelim(4, bd, pa[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, da, pb[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, ab, pd[2], temp8a); + abdlen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, abd); + + temp8alen = scale_expansion_zeroelim(4, ce, pb[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, eb, pc[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, bc, pe[2], temp8a); + bcelen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, bce); + + temp8alen = scale_expansion_zeroelim(4, da, pc[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, pd[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, cd, pa[2], temp8a); + cdalen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, cda); + + temp8alen = scale_expansion_zeroelim(4, eb, pd[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, pe[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, de, pb[2], temp8a); + deblen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, deb); + + temp8alen = scale_expansion_zeroelim(4, ac, pe[2], temp8a); + temp8blen = scale_expansion_zeroelim(4, ce, pa[2], temp8b); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp8alen = scale_expansion_zeroelim(4, ea, pc[2], temp8a); + eaclen = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp16len, temp16, eac); + + temp48alen = fast_expansion_sum_zeroelim(cdelen, cde, bcelen, bce, temp48a); + temp48blen = fast_expansion_sum_zeroelim(deblen, deb, bcdlen, bcd, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + bcdelen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, bcde); + xlen = scale_expansion_zeroelim(bcdelen, bcde, pa[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pa[0], det384x); + ylen = scale_expansion_zeroelim(bcdelen, bcde, pa[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pa[1], det384y); + zlen = scale_expansion_zeroelim(bcdelen, bcde, pa[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pa[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + alen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, adet); + + temp48alen = fast_expansion_sum_zeroelim(dealen, dea, cdalen, cda, temp48a); + temp48blen = fast_expansion_sum_zeroelim(eaclen, eac, cdelen, cde, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + cdealen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, cdea); + xlen = scale_expansion_zeroelim(cdealen, cdea, pb[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pb[0], det384x); + ylen = scale_expansion_zeroelim(cdealen, cdea, pb[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pb[1], det384y); + zlen = scale_expansion_zeroelim(cdealen, cdea, pb[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pb[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + blen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, bdet); + + temp48alen = fast_expansion_sum_zeroelim(eablen, eab, deblen, deb, temp48a); + temp48blen = fast_expansion_sum_zeroelim(abdlen, abd, dealen, dea, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + deablen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, deab); + xlen = scale_expansion_zeroelim(deablen, deab, pc[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pc[0], det384x); + ylen = scale_expansion_zeroelim(deablen, deab, pc[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pc[1], det384y); + zlen = scale_expansion_zeroelim(deablen, deab, pc[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pc[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + clen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, cdet); + + temp48alen = fast_expansion_sum_zeroelim(abclen, abc, eaclen, eac, temp48a); + temp48blen = fast_expansion_sum_zeroelim(bcelen, bce, eablen, eab, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + eabclen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, eabc); + xlen = scale_expansion_zeroelim(eabclen, eabc, pd[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pd[0], det384x); + ylen = scale_expansion_zeroelim(eabclen, eabc, pd[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pd[1], det384y); + zlen = scale_expansion_zeroelim(eabclen, eabc, pd[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pd[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + dlen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, ddet); + + temp48alen = fast_expansion_sum_zeroelim(bcdlen, bcd, abdlen, abd, temp48a); + temp48blen = fast_expansion_sum_zeroelim(cdalen, cda, abclen, abc, temp48b); + for (i = 0; i < temp48blen; i++) { + temp48b[i] = -temp48b[i]; + } + abcdlen = fast_expansion_sum_zeroelim(temp48alen, temp48a, temp48blen, temp48b, abcd); + xlen = scale_expansion_zeroelim(abcdlen, abcd, pe[0], temp192); + xlen = scale_expansion_zeroelim(xlen, temp192, pe[0], det384x); + ylen = scale_expansion_zeroelim(abcdlen, abcd, pe[1], temp192); + ylen = scale_expansion_zeroelim(ylen, temp192, pe[1], det384y); + zlen = scale_expansion_zeroelim(abcdlen, abcd, pe[2], temp192); + zlen = scale_expansion_zeroelim(zlen, temp192, pe[2], det384z); + xylen = fast_expansion_sum_zeroelim(xlen, det384x, ylen, det384y, detxy); + elen = fast_expansion_sum_zeroelim(xylen, detxy, zlen, det384z, edet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + cdelen = fast_expansion_sum_zeroelim(cdlen, cddet, elen, edet, cdedet); + deterlen = fast_expansion_sum_zeroelim(ablen, abdet, cdelen, cdedet, deter); + + return deter[deterlen - 1]; +} + +static double insphereadapt(const double *pa, + const double *pb, + const double *pc, + const double *pd, + const double *pe, + double permanent) +{ + INEXACT double aex, bex, cex, dex, aey, bey, cey, dey, aez, bez, cez, dez; + double det, errbound; + + INEXACT double aexbey1, bexaey1, bexcey1, cexbey1; + INEXACT double cexdey1, dexcey1, dexaey1, aexdey1; + INEXACT double aexcey1, cexaey1, bexdey1, dexbey1; + double aexbey0, bexaey0, bexcey0, cexbey0; + double cexdey0, dexcey0, dexaey0, aexdey0; + double aexcey0, cexaey0, bexdey0, dexbey0; + double ab[4], bc[4], cd[4], da[4], ac[4], bd[4]; + INEXACT double ab3, bc3, cd3, da3, ac3, bd3; + double abeps, bceps, cdeps, daeps, aceps, bdeps; + double temp8a[8], temp8b[8], temp8c[8], temp16[16], temp24[24], temp48[48]; + int temp8alen, temp8blen, temp8clen, temp16len, temp24len, temp48len; + double xdet[96], ydet[96], zdet[96], xydet[192]; + int xlen, ylen, zlen, xylen; + double adet[288], bdet[288], cdet[288], ddet[288]; + int alen, blen, clen, dlen; + double abdet[576], cddet[576]; + int ablen, cdlen; + double fin1[1152]; + int finlength; + + double aextail, bextail, cextail, dextail; + double aeytail, beytail, ceytail, deytail; + double aeztail, beztail, ceztail, deztail; + + INEXACT double bvirt; + double avirt, bround, around; + INEXACT double c; + INEXACT double abig; + double ahi, alo, bhi, blo; + double err1, err2, err3; + INEXACT double _i, _j; + double _0; + + aex = (double)(pa[0] - pe[0]); + bex = (double)(pb[0] - pe[0]); + cex = (double)(pc[0] - pe[0]); + dex = (double)(pd[0] - pe[0]); + aey = (double)(pa[1] - pe[1]); + bey = (double)(pb[1] - pe[1]); + cey = (double)(pc[1] - pe[1]); + dey = (double)(pd[1] - pe[1]); + aez = (double)(pa[2] - pe[2]); + bez = (double)(pb[2] - pe[2]); + cez = (double)(pc[2] - pe[2]); + dez = (double)(pd[2] - pe[2]); + + Two_Product(aex, bey, aexbey1, aexbey0); + Two_Product(bex, aey, bexaey1, bexaey0); + Two_Two_Diff(aexbey1, aexbey0, bexaey1, bexaey0, ab3, ab[2], ab[1], ab[0]); + ab[3] = ab3; + + Two_Product(bex, cey, bexcey1, bexcey0); + Two_Product(cex, bey, cexbey1, cexbey0); + Two_Two_Diff(bexcey1, bexcey0, cexbey1, cexbey0, bc3, bc[2], bc[1], bc[0]); + bc[3] = bc3; + + Two_Product(cex, dey, cexdey1, cexdey0); + Two_Product(dex, cey, dexcey1, dexcey0); + Two_Two_Diff(cexdey1, cexdey0, dexcey1, dexcey0, cd3, cd[2], cd[1], cd[0]); + cd[3] = cd3; + + Two_Product(dex, aey, dexaey1, dexaey0); + Two_Product(aex, dey, aexdey1, aexdey0); + Two_Two_Diff(dexaey1, dexaey0, aexdey1, aexdey0, da3, da[2], da[1], da[0]); + da[3] = da3; + + Two_Product(aex, cey, aexcey1, aexcey0); + Two_Product(cex, aey, cexaey1, cexaey0); + Two_Two_Diff(aexcey1, aexcey0, cexaey1, cexaey0, ac3, ac[2], ac[1], ac[0]); + ac[3] = ac3; + + Two_Product(bex, dey, bexdey1, bexdey0); + Two_Product(dex, bey, dexbey1, dexbey0); + Two_Two_Diff(bexdey1, bexdey0, dexbey1, dexbey0, bd3, bd[2], bd[1], bd[0]); + bd[3] = bd3; + + temp8alen = scale_expansion_zeroelim(4, cd, bez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, -cez, temp8b); + temp8clen = scale_expansion_zeroelim(4, bc, dez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -aex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -aey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, aez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -aez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + alen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, adet); + + temp8alen = scale_expansion_zeroelim(4, da, cez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, dez, temp8b); + temp8clen = scale_expansion_zeroelim(4, cd, aez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, bex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, bey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, bez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, bez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + blen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, bdet); + + temp8alen = scale_expansion_zeroelim(4, ab, dez, temp8a); + temp8blen = scale_expansion_zeroelim(4, bd, aez, temp8b); + temp8clen = scale_expansion_zeroelim(4, da, bez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, -cex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, -cey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, cez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, -cez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + clen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, cdet); + + temp8alen = scale_expansion_zeroelim(4, bc, aez, temp8a); + temp8blen = scale_expansion_zeroelim(4, ac, -bez, temp8b); + temp8clen = scale_expansion_zeroelim(4, ab, cez, temp8c); + temp16len = fast_expansion_sum_zeroelim(temp8alen, temp8a, temp8blen, temp8b, temp16); + temp24len = fast_expansion_sum_zeroelim(temp8clen, temp8c, temp16len, temp16, temp24); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dex, temp48); + xlen = scale_expansion_zeroelim(temp48len, temp48, dex, xdet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dey, temp48); + ylen = scale_expansion_zeroelim(temp48len, temp48, dey, ydet); + temp48len = scale_expansion_zeroelim(temp24len, temp24, dez, temp48); + zlen = scale_expansion_zeroelim(temp48len, temp48, dez, zdet); + xylen = fast_expansion_sum_zeroelim(xlen, xdet, ylen, ydet, xydet); + dlen = fast_expansion_sum_zeroelim(xylen, xydet, zlen, zdet, ddet); + + ablen = fast_expansion_sum_zeroelim(alen, adet, blen, bdet, abdet); + cdlen = fast_expansion_sum_zeroelim(clen, cdet, dlen, ddet, cddet); + finlength = fast_expansion_sum_zeroelim(ablen, abdet, cdlen, cddet, fin1); + + det = estimate(finlength, fin1); + errbound = isperrboundB * permanent; + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + Two_Diff_Tail(pa[0], pe[0], aex, aextail); + Two_Diff_Tail(pa[1], pe[1], aey, aeytail); + Two_Diff_Tail(pa[2], pe[2], aez, aeztail); + Two_Diff_Tail(pb[0], pe[0], bex, bextail); + Two_Diff_Tail(pb[1], pe[1], bey, beytail); + Two_Diff_Tail(pb[2], pe[2], bez, beztail); + Two_Diff_Tail(pc[0], pe[0], cex, cextail); + Two_Diff_Tail(pc[1], pe[1], cey, ceytail); + Two_Diff_Tail(pc[2], pe[2], cez, ceztail); + Two_Diff_Tail(pd[0], pe[0], dex, dextail); + Two_Diff_Tail(pd[1], pe[1], dey, deytail); + Two_Diff_Tail(pd[2], pe[2], dez, deztail); + if ((aextail == 0.0) && (aeytail == 0.0) && (aeztail == 0.0) && (bextail == 0.0) && + (beytail == 0.0) && (beztail == 0.0) && (cextail == 0.0) && (ceytail == 0.0) && + (ceztail == 0.0) && (dextail == 0.0) && (deytail == 0.0) && (deztail == 0.0)) { + return det; + } + + errbound = isperrboundC * permanent + resulterrbound * Absolute(det); + abeps = (aex * beytail + bey * aextail) - (aey * bextail + bex * aeytail); + bceps = (bex * ceytail + cey * bextail) - (bey * cextail + cex * beytail); + cdeps = (cex * deytail + dey * cextail) - (cey * dextail + dex * ceytail); + daeps = (dex * aeytail + aey * dextail) - (dey * aextail + aex * deytail); + aceps = (aex * ceytail + cey * aextail) - (aey * cextail + cex * aeytail); + bdeps = (bex * deytail + dey * bextail) - (bey * dextail + dex * beytail); + det += + (((bex * bex + bey * bey + bez * bez) * ((cez * daeps + dez * aceps + aez * cdeps) + + (ceztail * da3 + deztail * ac3 + aeztail * cd3)) + + (dex * dex + dey * dey + dez * dez) * ((aez * bceps - bez * aceps + cez * abeps) + + (aeztail * bc3 - beztail * ac3 + ceztail * ab3))) - + ((aex * aex + aey * aey + aez * aez) * ((bez * cdeps - cez * bdeps + dez * bceps) + + (beztail * cd3 - ceztail * bd3 + deztail * bc3)) + + (cex * cex + cey * cey + cez * cez) * ((dez * abeps + aez * bdeps + bez * daeps) + + (deztail * ab3 + aeztail * bd3 + beztail * da3)))) + + 2.0 * + (((bex * bextail + bey * beytail + bez * beztail) * (cez * da3 + dez * ac3 + aez * cd3) + + (dex * dextail + dey * deytail + dez * deztail) * + (aez * bc3 - bez * ac3 + cez * ab3)) - + ((aex * aextail + aey * aeytail + aez * aeztail) * (bez * cd3 - cez * bd3 + dez * bc3) + + (cex * cextail + cey * ceytail + cez * ceztail) * + (dez * ab3 + aez * bd3 + bez * da3))); + if ((det >= errbound) || (-det >= errbound)) { + return det; + } + + return insphereexact(pa, pb, pc, pd, pe); +} + +double insphere( + const double *pa, const double *pb, const double *pc, const double *pd, const double *pe) +{ + double aex, bex, cex, dex; + double aey, bey, cey, dey; + double aez, bez, cez, dez; + double aexbey, bexaey, bexcey, cexbey, cexdey, dexcey, dexaey, aexdey; + double aexcey, cexaey, bexdey, dexbey; + double alift, blift, clift, dlift; + double ab, bc, cd, da, ac, bd; + double abc, bcd, cda, dab; + double aezplus, bezplus, cezplus, dezplus; + double aexbeyplus, bexaeyplus, bexceyplus, cexbeyplus; + double cexdeyplus, dexceyplus, dexaeyplus, aexdeyplus; + double aexceyplus, cexaeyplus, bexdeyplus, dexbeyplus; + double det; + double permanent, errbound; + + aex = pa[0] - pe[0]; + bex = pb[0] - pe[0]; + cex = pc[0] - pe[0]; + dex = pd[0] - pe[0]; + aey = pa[1] - pe[1]; + bey = pb[1] - pe[1]; + cey = pc[1] - pe[1]; + dey = pd[1] - pe[1]; + aez = pa[2] - pe[2]; + bez = pb[2] - pe[2]; + cez = pc[2] - pe[2]; + dez = pd[2] - pe[2]; + + aexbey = aex * bey; + bexaey = bex * aey; + ab = aexbey - bexaey; + bexcey = bex * cey; + cexbey = cex * bey; + bc = bexcey - cexbey; + cexdey = cex * dey; + dexcey = dex * cey; + cd = cexdey - dexcey; + dexaey = dex * aey; + aexdey = aex * dey; + da = dexaey - aexdey; + + aexcey = aex * cey; + cexaey = cex * aey; + ac = aexcey - cexaey; + bexdey = bex * dey; + dexbey = dex * bey; + bd = bexdey - dexbey; + + abc = aez * bc - bez * ac + cez * ab; + bcd = bez * cd - cez * bd + dez * bc; + cda = cez * da + dez * ac + aez * cd; + dab = dez * ab + aez * bd + bez * da; + + alift = aex * aex + aey * aey + aez * aez; + blift = bex * bex + bey * bey + bez * bez; + clift = cex * cex + cey * cey + cez * cez; + dlift = dex * dex + dey * dey + dez * dez; + + det = (dlift * abc - clift * dab) + (blift * cda - alift * bcd); + + aezplus = Absolute(aez); + bezplus = Absolute(bez); + cezplus = Absolute(cez); + dezplus = Absolute(dez); + aexbeyplus = Absolute(aexbey); + bexaeyplus = Absolute(bexaey); + bexceyplus = Absolute(bexcey); + cexbeyplus = Absolute(cexbey); + cexdeyplus = Absolute(cexdey); + dexceyplus = Absolute(dexcey); + dexaeyplus = Absolute(dexaey); + aexdeyplus = Absolute(aexdey); + aexceyplus = Absolute(aexcey); + cexaeyplus = Absolute(cexaey); + bexdeyplus = Absolute(bexdey); + dexbeyplus = Absolute(dexbey); + permanent = ((cexdeyplus + dexceyplus) * bezplus + (dexbeyplus + bexdeyplus) * cezplus + + (bexceyplus + cexbeyplus) * dezplus) * + alift + + ((dexaeyplus + aexdeyplus) * cezplus + (aexceyplus + cexaeyplus) * dezplus + + (cexdeyplus + dexceyplus) * aezplus) * + blift + + ((aexbeyplus + bexaeyplus) * dezplus + (bexdeyplus + dexbeyplus) * aezplus + + (dexaeyplus + aexdeyplus) * bezplus) * + clift + + ((bexceyplus + cexbeyplus) * aezplus + (cexaeyplus + aexceyplus) * bezplus + + (aexbeyplus + bexaeyplus) * cezplus) * + dlift; + errbound = isperrboundA * permanent; + if ((det > errbound) || (-det > errbound)) { + return det; + } + + return insphereadapt(pa, pb, pc, pd, pe, permanent); +} + +} /* namespace robust_pred */ + +static int sgn(double x) +{ + return (x > 0) ? 1 : ((x < 0) ? -1 : 0); +} + +int orient2d(const double2 &a, const double2 &b, const double2 &c) +{ + return sgn(blender::robust_pred::orient2d(a, b, c)); +} + +int orient2d_fast(const double2 &a, const double2 &b, const double2 &c) +{ + return sgn(blender::robust_pred::orient2dfast(a, b, c)); +} + +int incircle(const double2 &a, const double2 &b, const double2 &c, const double2 &d) +{ + return sgn(robust_pred::incircle(a, b, c, d)); +} + +int incircle_fast(const double2 &a, const double2 &b, const double2 &c, const double2 &d) +{ + return sgn(robust_pred::incirclefast(a, b, c, d)); +} + +int orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d) +{ + return sgn(robust_pred::orient3d(a, b, c, d)); +} + +int orient3d_fast(const double3 &a, const double3 &b, const double3 &c, const double3 &d) +{ + return sgn(robust_pred::orient3dfast(a, b, c, d)); +} + +int insphere( + const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e) +{ + return sgn(robust_pred::insphere(a, b, c, d, e)); +} + +int insphere_fast( + const double3 &a, const double3 &b, const double3 &c, const double3 &d, const double3 &e) +{ + return sgn(robust_pred::inspherefast(a, b, c, d, e)); +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index fadd7d83444..f523bd07c09 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -3118,6 +3118,115 @@ void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4]) } } +/* -------------------------------------------------------------------- */ +/** \name Invert (Safe Orthographic) + * + * Invert the matrix, filling in zeroed axes using the valid ones where possible. + * + * Unlike #invert_m4_m4_safe set degenerate axis unit length instead of adding a small value, + * which has the results in: + * + * - Scaling by a large value on the resulting matrix. + * - Changing axis which aren't degenerate. + * + * \note We could support passing in a length value if there is a good use-case + * where we want to specify the length of the degenerate axes. + * \{ */ + +/** + * Return true if invert should be attempted again. + * + * \note Takes an array of points to be usable from 3x3 and 4x4 matrices. + */ +static bool invert_m3_m3_safe_ortho_prepare(float *mat[3]) +{ + enum { X = 1 << 0, Y = 1 << 1, Z = 1 << 2 }; + int flag = 0; + for (int i = 0; i < 3; i++) { + flag |= (len_squared_v3(mat[i]) == 0.0f) ? (1 << i) : 0; + } + + /* Either all or none are zero, either way we can't properly resolve this + * since we need to fill invalid axes from valid ones. */ + if (ELEM(flag, 0, X | Y | Z)) { + return false; + } + + switch (flag) { + case X | Y: { + ortho_v3_v3(mat[1], mat[2]); + ATTR_FALLTHROUGH; + } + case X: { + cross_v3_v3v3(mat[0], mat[1], mat[2]); + break; + } + + case Y | Z: { + ortho_v3_v3(mat[2], mat[0]); + ATTR_FALLTHROUGH; + } + case Y: { + cross_v3_v3v3(mat[1], mat[0], mat[2]); + break; + } + + case Z | X: { + ortho_v3_v3(mat[0], mat[1]); + ATTR_FALLTHROUGH; + } + case Z: { + cross_v3_v3v3(mat[2], mat[0], mat[1]); + break; + } + default: { + BLI_assert(0); /* Unreachable! */ + } + } + + for (int i = 0; i < 3; i++) { + if (flag & (1 << i)) { + if (UNLIKELY(normalize_v3(mat[i]) == 0.0f)) { + mat[i][i] = 1.0f; + } + } + } + + return true; +} + +/** + * A safe version of invert that uses valid axes, calculating the zero'd axis + * based on the non-zero ones. + * + * This works well for transformation matrices, when a single axis is zerod. + */ +void invert_m4_m4_safe_ortho(float Ainv[4][4], const float A[4][4]) +{ + if (UNLIKELY(!invert_m4_m4(Ainv, A))) { + float Atemp[4][4]; + copy_m4_m4(Atemp, A); + if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) && + invert_m4_m4(Ainv, Atemp)))) { + unit_m4(Ainv); + } + } +} + +void invert_m3_m3_safe_ortho(float Ainv[3][3], const float A[3][3]) +{ + if (UNLIKELY(!invert_m3_m3(Ainv, A))) { + float Atemp[3][3]; + copy_m3_m3(Atemp, A); + if (UNLIKELY(!(invert_m3_m3_safe_ortho_prepare((float *[3]){UNPACK3(Atemp)}) && + invert_m3_m3(Ainv, Atemp)))) { + unit_m3(Ainv); + } + } +} + +/** \} */ + /** * #SpaceTransform struct encapsulates all needed data to convert between two coordinate spaces * (where conversion can be represented by a matrix multiplication). diff --git a/source/blender/blenlib/intern/math_vec.cc b/source/blender/blenlib/intern/math_vec.cc new file mode 100644 index 00000000000..54926f84eb9 --- /dev/null +++ b/source/blender/blenlib/intern/math_vec.cc @@ -0,0 +1,195 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + */ + +#include "BLI_double2.hh" +#include "BLI_double3.hh" +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_hash.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mpq2.hh" +#include "BLI_mpq3.hh" +#include "BLI_span.hh" +#include "BLI_utildefines.h" + +namespace blender { + +float2::isect_result float2::isect_seg_seg(const float2 &v1, + const float2 &v2, + const float2 &v3, + const float2 &v4) +{ + float2::isect_result ans; + float div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]); + if (div == 0.0f) { + ans.lambda = 0.0f; + ans.mu = 0.0f; + ans.kind = float2::isect_result::LINE_LINE_COLINEAR; + } + else { + ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div; + ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div; + if (ans.lambda >= 0.0f && ans.lambda <= 1.0f && ans.mu >= 0.0f && ans.mu <= 1.0f) { + if (ans.lambda == 0.0f || ans.lambda == 1.0f || ans.mu == 0.0f || ans.mu == 1.0f) { + ans.kind = float2::isect_result::LINE_LINE_EXACT; + } + else { + ans.kind = float2::isect_result::LINE_LINE_CROSS; + } + } + else { + ans.kind = float2::isect_result::LINE_LINE_NONE; + } + } + return ans; +} + +double2::isect_result double2::isect_seg_seg(const double2 &v1, + const double2 &v2, + const double2 &v3, + const double2 &v4) +{ + double2::isect_result ans; + double div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]); + if (div == 0.0) { + ans.lambda = 0.0; + ans.mu = 0.0; + ans.kind = double2::isect_result::LINE_LINE_COLINEAR; + } + else { + ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div; + ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div; + if (ans.lambda >= 0.0 && ans.lambda <= 1.0 && ans.mu >= 0.0 && ans.mu <= 1.0) { + if (ans.lambda == 0.0 || ans.lambda == 1.0 || ans.mu == 0.0 || ans.mu == 1.0) { + ans.kind = double2::isect_result::LINE_LINE_EXACT; + } + else { + ans.kind = double2::isect_result::LINE_LINE_CROSS; + } + } + else { + ans.kind = double2::isect_result::LINE_LINE_NONE; + } + } + return ans; +} + +#ifdef WITH_GMP +mpq2::isect_result mpq2::isect_seg_seg(const mpq2 &v1, + const mpq2 &v2, + const mpq2 &v3, + const mpq2 &v4) +{ + mpq2::isect_result ans; + mpq_class div = (v2[0] - v1[0]) * (v4[1] - v3[1]) - (v2[1] - v1[1]) * (v4[0] - v3[0]); + if (div == 0.0) { + ans.lambda = 0.0; + ans.mu = 0.0; + ans.kind = mpq2::isect_result::LINE_LINE_COLINEAR; + } + else { + ans.lambda = ((v1[1] - v3[1]) * (v4[0] - v3[0]) - (v1[0] - v3[0]) * (v4[1] - v3[1])) / div; + ans.mu = ((v1[1] - v3[1]) * (v2[0] - v1[0]) - (v1[0] - v3[0]) * (v2[1] - v1[1])) / div; + if (ans.lambda >= 0 && ans.lambda <= 1 && ans.mu >= 0 && ans.mu <= 1) { + if (ans.lambda == 0 || ans.lambda == 1 || ans.mu == 0 || ans.mu == 1) { + ans.kind = mpq2::isect_result::LINE_LINE_EXACT; + } + else { + ans.kind = mpq2::isect_result::LINE_LINE_CROSS; + } + } + else { + ans.kind = mpq2::isect_result::LINE_LINE_NONE; + } + } + return ans; +} +#endif + +double3 double3::cross_poly(Span<double3> poly) +{ + /* Newell's Method. */ + int nv = static_cast<int>(poly.size()); + if (nv < 3) { + return double3(0, 0, 0); + } + const double3 *v_prev = &poly[nv - 1]; + const double3 *v_curr = &poly[0]; + double3 n(0, 0, 0); + for (int i = 0; i < nv;) { + n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]); + n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]); + n[2] = n[2] + ((*v_prev)[0] - (*v_curr)[0]) * ((*v_prev)[1] + (*v_curr)[1]); + v_prev = v_curr; + ++i; + if (i < nv) { + v_curr = &poly[i]; + } + } + return n; +} + +#ifdef WITH_GMP +mpq3 mpq3::cross_poly(Span<mpq3> poly) +{ + /* Newell's Method. */ + int nv = static_cast<int>(poly.size()); + if (nv < 3) { + return mpq3(0); + } + const mpq3 *v_prev = &poly[nv - 1]; + const mpq3 *v_curr = &poly[0]; + mpq3 n(0); + for (int i = 0; i < nv;) { + n[0] = n[0] + ((*v_prev)[1] - (*v_curr)[1]) * ((*v_prev)[2] + (*v_curr)[2]); + n[1] = n[1] + ((*v_prev)[2] - (*v_curr)[2]) * ((*v_prev)[0] + (*v_curr)[0]); + n[2] = n[2] + ((*v_prev)[0] - (*v_curr)[0]) * ((*v_prev)[1] + (*v_curr)[1]); + v_prev = v_curr; + ++i; + if (i < nv) { + v_curr = &poly[i]; + } + } + return n; +} + +uint64_t hash_mpq_class(const mpq_class &value) +{ + /* TODO: better/faster implementation of this. */ + return DefaultHash<float>{}(static_cast<float>(value.get_d())); +} + +uint64_t mpq2::hash() const +{ + uint64_t hashx = hash_mpq_class(this->x); + uint64_t hashy = hash_mpq_class(this->y); + return hashx ^ (hashy * 33); +} + +uint64_t mpq3::hash() const +{ + uint64_t hashx = hash_mpq_class(this->x); + uint64_t hashy = hash_mpq_class(this->y); + uint64_t hashz = hash_mpq_class(this->z); + return hashx ^ (hashy * 33) ^ (hashz * 33 * 37); +} +#endif + +} // namespace blender diff --git a/source/blender/blenlib/intern/math_vector.c b/source/blender/blenlib/intern/math_vector.c index 909d508e262..fb3ea539df1 100644 --- a/source/blender/blenlib/intern/math_vector.c +++ b/source/blender/blenlib/intern/math_vector.c @@ -669,6 +669,15 @@ void project_v3_v3v3(float out[3], const float p[3], const float v_proj[3]) out[2] = mul * v_proj[2]; } +void project_v3_v3v3_db(double out[3], const double p[3], const double v_proj[3]) +{ + const double mul = dot_v3v3_db(p, v_proj) / dot_v3v3_db(v_proj, v_proj); + + out[0] = mul * v_proj[0]; + out[1] = mul * v_proj[1]; + out[2] = mul * v_proj[2]; +} + /** * Project \a p onto a unit length \a v_proj */ @@ -796,6 +805,17 @@ void reflect_v3_v3v3(float out[3], const float v[3], const float normal[3]) out[2] = v[2] - (dot2 * normal[2]); } +void reflect_v3_v3v3_db(double out[3], const double v[3], const double normal[3]) +{ + const double dot2 = 2.0 * dot_v3v3_db(v, normal); + + /* BLI_ASSERT_UNIT_V3_DB(normal); this assert is not known? */ + + out[0] = v[0] - (dot2 * normal[0]); + out[1] = v[1] - (dot2 * normal[1]); + out[2] = v[2] - (dot2 * normal[2]); +} + /** * Takes a vector and computes 2 orthogonal directions. * diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 1b47832589e..598a22f6aa3 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -571,6 +571,13 @@ MINLINE void mul_v3_v3fl(float r[3], const float a[3], float f) r[2] = a[2] * f; } +MINLINE void mul_v3_v3db_db(double r[3], const double a[3], double f) +{ + r[0] = a[0] * f; + r[1] = a[1] * f; + r[2] = a[2] * f; +} + MINLINE void mul_v2_v2(float r[2], const float a[2]) { r[0] *= a[0]; @@ -988,6 +995,11 @@ MINLINE float len_squared_v3(const float v[3]) return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; } +MINLINE double len_squared_v3_db(const double v[3]) +{ + return v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; +} + MINLINE float len_manhattan_v2(const float v[2]) { return fabsf(v[0]) + fabsf(v[1]); @@ -1045,6 +1057,11 @@ MINLINE float len_v3(const float a[3]) return sqrtf(dot_v3v3(a, a)); } +MINLINE double len_v3_db(const double a[3]) +{ + return sqrt(dot_v3v3_db(a, a)); +} + MINLINE float len_squared_v2v2(const float a[2], const float b[2]) { float d[2]; @@ -1161,7 +1178,29 @@ MINLINE float normalize_v3_v3(float r[3], const float a[3]) return normalize_v3_v3_length(r, a, 1.0f); } -MINLINE double normalize_v3_length_d(double n[3], const double unit_length) +MINLINE double normalize_v3_v3_length_db(double r[3], const double a[3], double unit_length) +{ + double d = dot_v3v3_db(a, a); + + /* a larger value causes normalize errors in a + * scaled down models with camera extreme close */ + if (d > 1.0e-70) { + d = sqrt(d); + mul_v3_v3db_db(r, a, unit_length / d); + } + else { + zero_v3_db(r); + d = 0.0; + } + + return d; +} +MINLINE double normalize_v3_v3_db(double r[3], const double a[3]) +{ + return normalize_v3_v3_length_db(r, a, 1.0); +} + +MINLINE double normalize_v3_length_db(double n[3], const double unit_length) { double d = n[0] * n[0] + n[1] * n[1] + n[2] * n[2]; @@ -1184,9 +1223,9 @@ MINLINE double normalize_v3_length_d(double n[3], const double unit_length) return d; } -MINLINE double normalize_v3_d(double n[3]) +MINLINE double normalize_v3_db(double n[3]) { - return normalize_v3_length_d(n, 1.0); + return normalize_v3_length_db(n, 1.0); } MINLINE float normalize_v3_length(float n[3], const float unit_length) diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc new file mode 100644 index 00000000000..bb8b14ebdc6 --- /dev/null +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -0,0 +1,3382 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + */ + +#ifdef WITH_GMP + +# include <algorithm> +# include <fstream> +# include <iostream> + +# include "BLI_array.hh" +# include "BLI_assert.h" +# include "BLI_delaunay_2d.h" +# include "BLI_hash.hh" +# include "BLI_map.hh" +# include "BLI_math.h" +# include "BLI_math_boolean.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mesh_intersect.hh" +# include "BLI_mpq3.hh" +# include "BLI_set.hh" +# include "BLI_span.hh" +# include "BLI_stack.hh" +# include "BLI_vector.hh" +# include "BLI_vector_set.hh" + +# include "BLI_mesh_boolean.hh" + +namespace blender::meshintersect { + +/** + * Edge as two `const` Vert *'s, in a canonical order (lower vert id first). + * We use the Vert id field for hashing to get algorithms + * that yield predictable results from run-to-run and machine-to-machine. + */ +class Edge { + const Vert *v_[2]{nullptr, nullptr}; + + public: + Edge() = default; + Edge(const Vert *v0, const Vert *v1) + { + if (v0->id <= v1->id) { + v_[0] = v0; + v_[1] = v1; + } + else { + v_[0] = v1; + v_[1] = v0; + } + } + + const Vert *v0() const + { + return v_[0]; + } + + const Vert *v1() const + { + return v_[1]; + } + + const Vert *operator[](int i) const + { + return v_[i]; + } + + bool operator==(Edge other) const + { + return v_[0]->id == other.v_[0]->id && v_[1]->id == other.v_[1]->id; + } + + uint64_t hash() const + { + constexpr uint64_t h1 = 33; + uint64_t v0hash = DefaultHash<int>{}(v_[0]->id); + uint64_t v1hash = DefaultHash<int>{}(v_[1]->id); + return v0hash ^ (v1hash * h1); + } +}; + +static std::ostream &operator<<(std::ostream &os, const Edge &e) +{ + if (e.v0() == nullptr) { + BLI_assert(e.v1() == nullptr); + os << "(null,null)"; + } + else { + os << "(" << e.v0() << "," << e.v1() << ")"; + } + return os; +} + +static std::ostream &operator<<(std::ostream &os, const Span<int> &a) +{ + for (int i : a.index_range()) { + os << a[i]; + if (i != a.size() - 1) { + os << " "; + } + } + return os; +} + +static std::ostream &operator<<(std::ostream &os, const Array<int> &iarr) +{ + os << Span<int>(iarr); + return os; +} + +/** Holds information about topology of an #IMesh that is all triangles. */ +class TriMeshTopology : NonCopyable { + /** Triangles that contain a given Edge (either order). */ + Map<Edge, Vector<int> *> edge_tri_; + /** Edges incident on each vertex. */ + Map<const Vert *, Vector<Edge>> vert_edges_; + + public: + TriMeshTopology(const IMesh &tm); + ~TriMeshTopology(); + + /* If e is manifold, return index of the other triangle (not t) that has it. + * Else return NO_INDEX. */ + int other_tri_if_manifold(Edge e, int t) const + { + if (edge_tri_.contains(e)) { + auto *p = edge_tri_.lookup(e); + if (p->size() == 2) { + return ((*p)[0] == t) ? (*p)[1] : (*p)[0]; + } + } + return NO_INDEX; + } + + /* Which triangles share edge e (in either orientation)? */ + const Vector<int> *edge_tris(Edge e) const + { + return edge_tri_.lookup_default(e, nullptr); + } + + /* Which edges are incident on the given vertex? + * We assume v has some incident edges. */ + const Vector<Edge> &vert_edges(const Vert *v) const + { + return vert_edges_.lookup(v); + } + + Map<Edge, Vector<int> *>::ItemIterator edge_tri_map_items() const + { + return edge_tri_.items(); + } +}; + +TriMeshTopology::TriMeshTopology(const IMesh &tm) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "TRIMESHTOPOLOGY CONSTRUCTION\n"; + } + /* If everything were manifold, `F+V-E=2` and `E=3F/2`. + * So an likely overestimate, allowing for non-manifoldness, is `E=2F` and `V=F`. */ + const int estimate_num_edges = 2 * tm.face_size(); + const int estimate_num_verts = tm.face_size(); + edge_tri_.reserve(estimate_num_edges); + vert_edges_.reserve(estimate_num_verts); + for (int t : tm.face_index_range()) { + const Face &tri = *tm.face(t); + BLI_assert(tri.is_tri()); + for (int i = 0; i < 3; ++i) { + const Vert *v = tri[i]; + const Vert *vnext = tri[(i + 1) % 3]; + Edge e(v, vnext); + Vector<Edge> *edges = vert_edges_.lookup_ptr(v); + if (edges == nullptr) { + vert_edges_.add_new(v, Vector<Edge>()); + edges = vert_edges_.lookup_ptr(v); + BLI_assert(edges != nullptr); + } + edges->append_non_duplicates(e); + auto createf = [t](Vector<int> **pvec) { *pvec = new Vector<int>{t}; }; + auto modifyf = [t](Vector<int> **pvec) { (*pvec)->append_non_duplicates(t); }; + this->edge_tri_.add_or_modify(Edge(v, vnext), createf, modifyf); + } + } + /* Debugging. */ + if (dbg_level > 0) { + std::cout << "After TriMeshTopology construction\n"; + for (auto item : edge_tri_.items()) { + std::cout << "tris for edge " << item.key << ": " << *item.value << "\n"; + constexpr bool print_stats = false; + if (print_stats) { + edge_tri_.print_stats(); + } + } + for (auto item : vert_edges_.items()) { + std::cout << "edges for vert " << item.key << ":\n"; + for (const Edge &e : item.value) { + std::cout << " " << e << "\n"; + } + std::cout << "\n"; + } + } +} + +TriMeshTopology::~TriMeshTopology() +{ + for (const Vector<int> *vec : edge_tri_.values()) { + delete vec; + } +} + +/** A Patch is a maximal set of triangles that share manifold edges only. */ +class Patch { + Vector<int> tri_; /* Indices of triangles in the Patch. */ + + public: + Patch() = default; + + void add_tri(int t) + { + tri_.append(t); + } + + int tot_tri() const + { + return tri_.size(); + } + + int tri(int i) const + { + return tri_[i]; + } + + IndexRange tri_range() const + { + return IndexRange(tri_.size()); + } + + Span<int> tris() const + { + return Span<int>(tri_); + } + + int cell_above{NO_INDEX}; + int cell_below{NO_INDEX}; + int component{NO_INDEX}; +}; + +static std::ostream &operator<<(std::ostream &os, const Patch &patch) +{ + os << "Patch " << patch.tris(); + if (patch.cell_above != NO_INDEX) { + os << " cell_above=" << patch.cell_above; + } + else { + os << " cell_above not set"; + } + if (patch.cell_below != NO_INDEX) { + os << " cell_below=" << patch.cell_below; + } + else { + os << " cell_below not set"; + } + return os; +} + +class PatchesInfo { + /** All of the Patches for a #IMesh. */ + Vector<Patch> patch_; + /** Patch index for corresponding triangle. */ + Array<int> tri_patch_; + /** Shared edge for incident patches; (-1, -1) if none. */ + Map<std::pair<int, int>, Edge> pp_edge_; + + public: + explicit PatchesInfo(int ntri) + { + constexpr int max_expected_patch_patch_incidences = 100; + tri_patch_ = Array<int>(ntri, NO_INDEX); + pp_edge_.reserve(max_expected_patch_patch_incidences); + } + + int tri_patch(int t) const + { + return tri_patch_[t]; + } + + int add_patch() + { + int patch_index = patch_.append_and_get_index(Patch()); + return patch_index; + } + + void grow_patch(int patch_index, int t) + { + tri_patch_[t] = patch_index; + patch_[patch_index].add_tri(t); + } + + bool tri_is_assigned(int t) const + { + return tri_patch_[t] != NO_INDEX; + } + + const Patch &patch(int patch_index) const + { + return patch_[patch_index]; + } + + Patch &patch(int patch_index) + { + return patch_[patch_index]; + } + + int tot_patch() const + { + return patch_.size(); + } + + IndexRange index_range() const + { + return IndexRange(patch_.size()); + } + + const Patch *begin() const + { + return patch_.begin(); + } + + const Patch *end() const + { + return patch_.end(); + } + + Patch *begin() + { + return patch_.begin(); + } + + Patch *end() + { + return patch_.end(); + } + + void add_new_patch_patch_edge(int p1, int p2, Edge e) + { + pp_edge_.add_new(std::pair<int, int>(p1, p2), e); + pp_edge_.add_new(std::pair<int, int>(p2, p1), e); + } + + Edge patch_patch_edge(int p1, int p2) + { + return pp_edge_.lookup_default(std::pair<int, int>(p1, p2), Edge()); + } +}; + +static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding); + +/** + * A Cell is a volume of 3-space, surrounded by patches. + * We will partition all 3-space into Cells. + * One cell, the Ambient cell, contains all other cells. + */ +class Cell { + Vector<int> patches_; + Array<int> winding_; + int merged_to_{NO_INDEX}; + bool winding_assigned_{false}; + /* in_output_volume_ will be true when this cell should be in the output volume. */ + bool in_output_volume_{false}; + /* zero_volume_ will be true when this is a zero-volume cell (inside a stack of identical + * triangles). */ + bool zero_volume_{false}; + + public: + Cell() = default; + + void add_patch(int p) + { + patches_.append(p); + } + + void add_patch_non_duplicates(int p) + { + patches_.append_non_duplicates(p); + } + + const Span<int> patches() const + { + return Span<int>(patches_); + } + + const Span<int> winding() const + { + return Span<int>(winding_); + } + + void init_winding(int winding_len) + { + winding_ = Array<int>(winding_len); + } + + void seed_ambient_winding() + { + winding_.fill(0); + winding_assigned_ = true; + } + + void set_winding_and_in_output_volume(const Cell &from_cell, + int shape, + int delta, + BoolOpType bool_optype) + { + std::copy(from_cell.winding().begin(), from_cell.winding().end(), winding_.begin()); + winding_[shape] += delta; + winding_assigned_ = true; + in_output_volume_ = apply_bool_op(bool_optype, winding_); + } + + bool in_output_volume() const + { + return in_output_volume_; + } + + bool winding_assigned() const + { + return winding_assigned_; + } + + bool zero_volume() const + { + return zero_volume_; + } + + int merged_to() const + { + return merged_to_; + } + + void set_merged_to(int c) + { + merged_to_ = c; + } + + /** + * Call this when it is possible that this Cell has zero volume, + * and if it does, set zero_volume_ to true. + */ + void check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh); +}; + +static std::ostream &operator<<(std::ostream &os, const Cell &cell) +{ + os << "Cell patches " << cell.patches(); + if (cell.winding().size() > 0) { + os << " winding=" << cell.winding(); + os << " in_output_volume=" << cell.in_output_volume(); + } + os << " zv=" << cell.zero_volume(); + return os; +} + +static bool tris_have_same_verts(const IMesh &mesh, int t1, int t2) +{ + const Face &tri1 = *mesh.face(t1); + const Face &tri2 = *mesh.face(t2); + BLI_assert(tri1.size() == 3 && tri2.size() == 3); + if (tri1.vert[0] == tri2.vert[0]) { + return ((tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[2]) || + (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[1])); + } + if (tri1.vert[0] == tri2.vert[1]) { + return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[2]) || + (tri1.vert[1] == tri2.vert[2] && tri1.vert[2] == tri2.vert[0])); + } + if (tri1.vert[0] == tri2.vert[2]) { + return ((tri1.vert[1] == tri2.vert[0] && tri1.vert[2] == tri2.vert[1]) || + (tri1.vert[1] == tri2.vert[1] && tri1.vert[2] == tri2.vert[0])); + } + return false; +} + +/** + * A Cell will have zero volume if it is bounded by exactly two patches and those + * patches are geometrically identical triangles (perhaps flipped versions of each other). + * If this Cell has zero volume, set its zero_volume_ member to true. + */ +void Cell::check_for_zero_volume(const PatchesInfo &pinfo, const IMesh &mesh) +{ + if (patches_.size() == 2) { + const Patch &p1 = pinfo.patch(patches_[0]); + const Patch &p2 = pinfo.patch(patches_[1]); + if (p1.tot_tri() == 1 && p2.tot_tri() == 1) { + if (tris_have_same_verts(mesh, p1.tri(0), p2.tri(0))) { + zero_volume_ = true; + } + } + } +} + +/* Information about all the Cells. */ +class CellsInfo { + Vector<Cell> cell_; + + public: + CellsInfo() = default; + + int add_cell() + { + int index = cell_.append_and_get_index(Cell()); + return index; + } + + Cell &cell(int c) + { + return cell_[c]; + } + + const Cell &cell(int c) const + { + return cell_[c]; + } + + int tot_cell() const + { + return cell_.size(); + } + + IndexRange index_range() const + { + return cell_.index_range(); + } + + const Cell *begin() const + { + return cell_.begin(); + } + + const Cell *end() const + { + return cell_.end(); + } + + Cell *begin() + { + return cell_.begin(); + } + + Cell *end() + { + return cell_.end(); + } + + void init_windings(int winding_len) + { + for (Cell &cell : cell_) { + cell.init_winding(winding_len); + } + } +}; + +/** + * For Debugging: write a .obj file showing the patch/cell structure or just the cells. + */ +static void write_obj_cell_patch(const IMesh &m, + const CellsInfo &cinfo, + const PatchesInfo &pinfo, + bool cells_only, + const std::string &name) +{ + /* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library. + * This is just for developer debugging anyway, + * and should never be called in production Blender. */ +# ifdef _WIN_32 + const char *objdir = BLI_getenv("HOME"); +# else + const char *objdir = "/tmp/"; +# endif + + std::string fname = std::string(objdir) + name + std::string("_cellpatch.obj"); + std::ofstream f; + f.open(fname); + if (!f) { + std::cout << "Could not open file " << fname << "\n"; + return; + } + + /* Copy IMesh so can populate verts. */ + IMesh mm = m; + mm.populate_vert(); + f << "o cellpatch\n"; + for (const Vert *v : mm.vertices()) { + const double3 dv = v->co; + f << "v " << dv[0] << " " << dv[1] << " " << dv[2] << "\n"; + } + if (!cells_only) { + for (int p : pinfo.index_range()) { + f << "g patch" << p << "\n"; + const Patch &patch = pinfo.patch(p); + for (int t : patch.tris()) { + const Face &tri = *mm.face(t); + f << "f "; + for (const Vert *v : tri) { + f << mm.lookup_vert(v) + 1 << " "; + } + f << "\n"; + } + } + } + for (int c : cinfo.index_range()) { + f << "g cell" << c << "\n"; + const Cell &cell = cinfo.cell(c); + for (int p : cell.patches()) { + const Patch &patch = pinfo.patch(p); + for (int t : patch.tris()) { + const Face &tri = *mm.face(t); + f << "f "; + for (const Vert *v : tri) { + f << mm.lookup_vert(v) + 1 << " "; + } + f << "\n"; + } + } + } + f.close(); +} + +static void merge_cells(int merge_to, int merge_from, CellsInfo &cinfo, PatchesInfo &pinfo) +{ + if (merge_to == merge_from) { + return; + } + Cell &merge_from_cell = cinfo.cell(merge_from); + Cell &merge_to_cell = cinfo.cell(merge_to); + int final_merge_to = merge_to; + while (merge_to_cell.merged_to() != NO_INDEX) { + final_merge_to = merge_to_cell.merged_to(); + merge_to_cell = cinfo.cell(final_merge_to); + } + for (Patch &patch : pinfo) { + if (patch.cell_above == merge_from) { + patch.cell_above = final_merge_to; + } + if (patch.cell_below == merge_from) { + patch.cell_below = final_merge_to; + } + } + for (int cell_p : merge_from_cell.patches()) { + merge_to_cell.add_patch_non_duplicates(cell_p); + } + merge_from_cell.set_merged_to(final_merge_to); +} + +/** + * Partition the triangles of \a tm into Patches. + */ +static PatchesInfo find_patches(const IMesh &tm, const TriMeshTopology &tmtopo) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nFIND_PATCHES\n"; + } + int ntri = tm.face_size(); + PatchesInfo pinfo(ntri); + /* Algorithm: Grow patches across manifold edges as long as there are unassigned triangles. */ + Stack<int> cur_patch_grow; + for (int t : tm.face_index_range()) { + if (pinfo.tri_patch(t) == -1) { + cur_patch_grow.push(t); + int cur_patch_index = pinfo.add_patch(); + while (!cur_patch_grow.is_empty()) { + int tcand = cur_patch_grow.pop(); + if (dbg_level > 1) { + std::cout << "pop tcand = " << tcand << "; assigned = " << pinfo.tri_is_assigned(tcand) + << "\n"; + } + if (pinfo.tri_is_assigned(tcand)) { + continue; + } + if (dbg_level > 1) { + std::cout << "grow patch from seed tcand=" << tcand << "\n"; + } + pinfo.grow_patch(cur_patch_index, tcand); + const Face &tri = *tm.face(tcand); + for (int i = 0; i < 3; ++i) { + Edge e(tri[i], tri[(i + 1) % 3]); + int t_other = tmtopo.other_tri_if_manifold(e, tcand); + if (dbg_level > 1) { + std::cout << " edge " << e << " generates t_other=" << t_other << "\n"; + } + if (t_other != NO_INDEX) { + if (!pinfo.tri_is_assigned(t_other)) { + if (dbg_level > 1) { + std::cout << " push t_other = " << t_other << "\n"; + } + cur_patch_grow.push(t_other); + } + } + else { + /* e is non-manifold. Set any patch-patch incidences we can. */ + if (dbg_level > 1) { + std::cout << " e non-manifold case\n"; + } + const Vector<int> *etris = tmtopo.edge_tris(e); + if (etris != nullptr) { + for (int i : etris->index_range()) { + int t_other = (*etris)[i]; + if (t_other != tcand && pinfo.tri_is_assigned(t_other)) { + int p_other = pinfo.tri_patch(t_other); + if (p_other == cur_patch_index) { + continue; + } + if (pinfo.patch_patch_edge(cur_patch_index, p_other).v0() == nullptr) { + pinfo.add_new_patch_patch_edge(cur_patch_index, p_other, e); + if (dbg_level > 1) { + std::cout << "added patch_patch_edge (" << cur_patch_index << "," << p_other + << ") = " << e << "\n"; + } + } + } + } + } + } + } + } + } + } + if (dbg_level > 0) { + std::cout << "\nafter FIND_PATCHES: found " << pinfo.tot_patch() << " patches\n"; + for (int p : pinfo.index_range()) { + std::cout << p << ": " << pinfo.patch(p) << "\n"; + } + if (dbg_level > 1) { + std::cout << "\ntriangle map\n"; + for (int t : tm.face_index_range()) { + std::cout << t << ": patch " << pinfo.tri_patch(t) << "\n"; + } + } + std::cout << "\npatch-patch incidences\n"; + for (int p1 : pinfo.index_range()) { + for (int p2 : pinfo.index_range()) { + Edge e = pinfo.patch_patch_edge(p1, p2); + if (e.v0() != nullptr) { + std::cout << "p" << p1 << " and p" << p2 << " share edge " << e << "\n"; + } + } + } + } + return pinfo; +} + +/** + * If e is an edge in tri, return the vertex that isn't part of tri, + * the "flap" vertex, or nullptr if e is not part of tri. + * Also, e may be reversed in tri. + * Set *r_rev to true if it is reversed, else false. + */ +static const Vert *find_flap_vert(const Face &tri, const Edge e, bool *r_rev) +{ + *r_rev = false; + const Vert *flapv; + if (tri[0] == e.v0()) { + if (tri[1] == e.v1()) { + *r_rev = false; + flapv = tri[2]; + } + else { + if (tri[2] != e.v1()) { + return nullptr; + } + *r_rev = true; + flapv = tri[1]; + } + } + else if (tri[1] == e.v0()) { + if (tri[2] == e.v1()) { + *r_rev = false; + flapv = tri[0]; + } + else { + if (tri[0] != e.v1()) { + return nullptr; + } + *r_rev = true; + flapv = tri[2]; + } + } + else { + if (tri[2] != e.v0()) { + return nullptr; + } + if (tri[0] == e.v1()) { + *r_rev = false; + flapv = tri[1]; + } + else { + if (tri[1] != e.v1()) { + return nullptr; + } + *r_rev = true; + flapv = tri[0]; + } + } + return flapv; +} + +/** + * Triangle \a tri and tri0 share edge e. + * Classify \a tri with respect to tri0 as described in + * sort_tris_around_edge, and return 1, 2, 3, or 4 as \a tri is: + * (1) co-planar with tri0 and on same side of e + * (2) co-planar with tri0 and on opposite side of e + * (3) below plane of tri0 + * (4) above plane of tri0 + * For "above" and "below", we use the orientation of non-reversed + * orientation of tri0. + * Because of the way the intersect mesh was made, we can assume + * that if a triangle is in class 1 then it is has the same flap vert + * as tri0. + */ +static int sort_tris_class(const Face &tri, const Face &tri0, const Edge e) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "classify e = " << e << "\n"; + } + mpq3 a0 = tri0[0]->co_exact; + mpq3 a1 = tri0[1]->co_exact; + mpq3 a2 = tri0[2]->co_exact; + bool rev; + bool rev0; + const Vert *flapv0 = find_flap_vert(tri0, e, &rev0); + const Vert *flapv = find_flap_vert(tri, e, &rev); + if (dbg_level > 0) { + std::cout << " t0 = " << tri0[0] << " " << tri0[1] << " " << tri0[2]; + std::cout << " rev0 = " << rev0 << " flapv0 = " << flapv0 << "\n"; + std::cout << " t = " << tri[0] << " " << tri[1] << " " << tri[2]; + std::cout << " rev = " << rev << " flapv = " << flapv << "\n"; + } + BLI_assert(flapv != nullptr && flapv0 != nullptr); + const mpq3 flap = flapv->co_exact; + /* orient will be positive if flap is below oriented plane of a0,a1,a2. */ + int orient = orient3d(a0, a1, a2, flap); + int ans; + if (orient > 0) { + ans = rev0 ? 4 : 3; + } + else if (orient < 0) { + ans = rev0 ? 3 : 4; + } + else { + ans = flapv == flapv0 ? 1 : 2; + } + if (dbg_level > 0) { + std::cout << " orient = " << orient << " ans = " << ans << "\n"; + } + return ans; +} + +constexpr int EXTRA_TRI_INDEX = INT_MAX; + +/** + * To ensure consistent ordering of co-planar triangles if they happen to be sorted around + * more than one edge, sort the triangle indices in g (in place) by their index -- but also apply + * a sign to the index: positive if the triangle has edge e in the same orientation, + * otherwise negative. + */ +static void sort_by_signed_triangle_index(Vector<int> &g, + const Edge e, + const IMesh &tm, + const Face *extra_tri) +{ + Array<int> signed_g(g.size()); + for (int i : g.index_range()) { + const Face &tri = g[i] == EXTRA_TRI_INDEX ? *extra_tri : *tm.face(g[i]); + bool rev; + find_flap_vert(tri, e, &rev); + signed_g[i] = rev ? -g[i] : g[i]; + } + std::sort(signed_g.begin(), signed_g.end()); + + for (int i : g.index_range()) { + g[i] = abs(signed_g[i]); + } +} + +/** + * Sort the triangles \a tris, which all share edge e, as they appear + * geometrically clockwise when looking down edge e. + * Triangle t0 is the first triangle in the top-level call + * to this recursive routine. The merge step below differs + * for the top level call and all the rest, so this distinguishes those cases. + * Care is taken in the case of duplicate triangles to have + * an ordering that is consistent with that which would happen + * if another edge of the triangle were sorted around. + * + * We sometimes need to do this with an extra triangle that is not part of tm. + * To accommodate this: + * If extra_tri is non-null, then an index of EXTRA_TRI_INDEX should use it for the triangle. + */ +static Array<int> sort_tris_around_edge(const IMesh &tm, + const TriMeshTopology &tmtopo, + const Edge e, + const Span<int> tris, + const int t0, + const Face *extra_tri) +{ + /* Divide and conquer, quick-sort-like sort. + * Pick a triangle t0, then partition into groups: + * (1) co-planar with t0 and on same side of e + * (2) co-planar with t0 and on opposite side of e + * (3) below plane of t0 + * (4) above plane of t0 + * Each group is sorted and then the sorts are merged to give the answer. + * We don't expect the input array to be very large - should typically + * be only 3 or 4 - so OK to make copies of arrays instead of swapping + * around in a single array. */ + const int dbg_level = 0; + if (tris.size() == 0) { + return Array<int>(); + } + if (dbg_level > 0) { + if (t0 == tris[0]) { + std::cout << "\n"; + } + std::cout << "sort_tris_around_edge " << e << "\n"; + std::cout << "tris = " << tris << "\n"; + std::cout << "t0 = " << t0 << "\n"; + } + Vector<int> g1{tris[0]}; + Vector<int> g2; + Vector<int> g3; + Vector<int> g4; + std::array<Vector<int> *, 4> groups = {&g1, &g2, &g3, &g4}; + const Face &triref = *tm.face(tris[0]); + for (int i : tris.index_range()) { + if (i == 0) { + continue; + } + int t = tris[i]; + BLI_assert(t < tm.face_size() || (t == EXTRA_TRI_INDEX && extra_tri != nullptr)); + const Face &tri = (t == EXTRA_TRI_INDEX) ? *extra_tri : *tm.face(t); + if (dbg_level > 2) { + std::cout << "classifying tri " << t << " with respect to " << tris[0] << "\n"; + } + int group_num = sort_tris_class(tri, triref, e); + if (dbg_level > 2) { + std::cout << " classify result : " << group_num << "\n"; + } + groups[group_num - 1]->append(t); + } + if (dbg_level > 1) { + std::cout << "g1 = " << g1 << "\n"; + std::cout << "g2 = " << g2 << "\n"; + std::cout << "g3 = " << g3 << "\n"; + std::cout << "g4 = " << g4 << "\n"; + } + if (g1.size() > 1) { + sort_by_signed_triangle_index(g1, e, tm, extra_tri); + if (dbg_level > 1) { + std::cout << "g1 sorted: " << g1 << "\n"; + } + } + if (g2.size() > 1) { + sort_by_signed_triangle_index(g2, e, tm, extra_tri); + if (dbg_level > 1) { + std::cout << "g2 sorted: " << g2 << "\n"; + } + } + if (g3.size() > 1) { + Array<int> g3sorted = sort_tris_around_edge(tm, tmtopo, e, g3, t0, extra_tri); + std::copy(g3sorted.begin(), g3sorted.end(), g3.begin()); + if (dbg_level > 1) { + std::cout << "g3 sorted: " << g3 << "\n"; + } + } + if (g4.size() > 1) { + Array<int> g4sorted = sort_tris_around_edge(tm, tmtopo, e, g4, t0, extra_tri); + std::copy(g4sorted.begin(), g4sorted.end(), g4.begin()); + if (dbg_level > 1) { + std::cout << "g4 sorted: " << g4 << "\n"; + } + } + int group_tot_size = g1.size() + g2.size() + g3.size() + g4.size(); + Array<int> ans(group_tot_size); + int *p = ans.begin(); + if (tris[0] == t0) { + p = std::copy(g1.begin(), g1.end(), p); + p = std::copy(g4.begin(), g4.end(), p); + p = std::copy(g2.begin(), g2.end(), p); + std::copy(g3.begin(), g3.end(), p); + } + else { + p = std::copy(g3.begin(), g3.end(), p); + p = std::copy(g1.begin(), g1.end(), p); + p = std::copy(g4.begin(), g4.end(), p); + std::copy(g2.begin(), g2.end(), p); + } + if (dbg_level > 0) { + std::cout << "sorted tris = " << ans << "\n"; + } + return ans; +} + +/** + * Find the Cells around edge e. + * This possibly makes new cells in \a cinfo, and sets up the + * bipartite graph edges between cells and patches. + * Will modify \a pinfo and \a cinfo and the patches and cells they contain. + */ +static void find_cells_from_edge(const IMesh &tm, + const TriMeshTopology &tmtopo, + PatchesInfo &pinfo, + CellsInfo &cinfo, + const Edge e) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_CELLS_FROM_EDGE " << e << "\n"; + } + const Vector<int> *edge_tris = tmtopo.edge_tris(e); + BLI_assert(edge_tris != nullptr); + Array<int> sorted_tris = sort_tris_around_edge( + tm, tmtopo, e, Span<int>(*edge_tris), (*edge_tris)[0], nullptr); + + int n_edge_tris = edge_tris->size(); + Array<int> edge_patches(n_edge_tris); + for (int i = 0; i < n_edge_tris; ++i) { + edge_patches[i] = pinfo.tri_patch(sorted_tris[i]); + if (dbg_level > 1) { + std::cout << "edge_patches[" << i << "] = " << edge_patches[i] << "\n"; + } + } + for (int i = 0; i < n_edge_tris; ++i) { + int inext = (i + 1) % n_edge_tris; + int r_index = edge_patches[i]; + int rnext_index = edge_patches[inext]; + Patch &r = pinfo.patch(r_index); + Patch &rnext = pinfo.patch(rnext_index); + bool r_flipped; + bool rnext_flipped; + find_flap_vert(*tm.face(sorted_tris[i]), e, &r_flipped); + find_flap_vert(*tm.face(sorted_tris[inext]), e, &rnext_flipped); + int *r_follow_cell = r_flipped ? &r.cell_below : &r.cell_above; + int *rnext_prev_cell = rnext_flipped ? &rnext.cell_above : &rnext.cell_below; + if (dbg_level > 0) { + std::cout << "process patch pair " << r_index << " " << rnext_index << "\n"; + std::cout << " r_flipped = " << r_flipped << " rnext_flipped = " << rnext_flipped << "\n"; + std::cout << " r_follow_cell (" << (r_flipped ? "below" : "above") + << ") = " << *r_follow_cell << "\n"; + std::cout << " rnext_prev_cell (" << (rnext_flipped ? "above" : "below") + << ") = " << *rnext_prev_cell << "\n"; + } + if (*r_follow_cell == NO_INDEX && *rnext_prev_cell == NO_INDEX) { + /* Neither is assigned: make a new cell. */ + int c = cinfo.add_cell(); + *r_follow_cell = c; + *rnext_prev_cell = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(r_index); + cell.add_patch(rnext_index); + cell.check_for_zero_volume(pinfo, tm); + if (dbg_level > 0) { + std::cout << " made new cell " << c << "\n"; + std::cout << " p" << r_index << "." << (r_flipped ? "cell_below" : "cell_above") << " = c" + << c << "\n"; + std::cout << " p" << rnext_index << "." << (rnext_flipped ? "cell_above" : "cell_below") + << " = c" << c << "\n"; + } + } + else if (*r_follow_cell != NO_INDEX && *rnext_prev_cell == NO_INDEX) { + int c = *r_follow_cell; + *rnext_prev_cell = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(rnext_index); + cell.check_for_zero_volume(pinfo, tm); + if (dbg_level > 0) { + std::cout << " reuse r_follow: p" << rnext_index << "." + << (rnext_flipped ? "cell_above" : "cell_below") << " = c" << c << "\n"; + } + } + else if (*r_follow_cell == NO_INDEX && *rnext_prev_cell != NO_INDEX) { + int c = *rnext_prev_cell; + *r_follow_cell = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(r_index); + cell.check_for_zero_volume(pinfo, tm); + if (dbg_level > 0) { + std::cout << " reuse rnext prev: rprev_p" << r_index << "." + << (r_flipped ? "cell_below" : "cell_above") << " = c" << c << "\n"; + } + } + else { + if (*r_follow_cell != *rnext_prev_cell) { + if (dbg_level > 0) { + std::cout << " merge cell " << *rnext_prev_cell << " into cell " << *r_follow_cell + << "\n"; + } + merge_cells(*r_follow_cell, *rnext_prev_cell, cinfo, pinfo); + } + } + } +} + +/** + * Find the partition of 3-space into Cells. + * This assigns the cell_above and cell_below for each Patch. + */ +static CellsInfo find_cells(const IMesh &tm, const TriMeshTopology &tmtopo, PatchesInfo &pinfo) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nFIND_CELLS\n"; + } + CellsInfo cinfo; + /* For each unique edge shared between patch pairs, process it. */ + Set<Edge> processed_edges; + int np = pinfo.tot_patch(); + for (int p = 0; p < np; ++p) { + for (int q = p + 1; q < np; ++q) { + Edge e = pinfo.patch_patch_edge(p, q); + if (e.v0() != nullptr) { + if (!processed_edges.contains(e)) { + processed_edges.add_new(e); + find_cells_from_edge(tm, tmtopo, pinfo, cinfo, e); + } + } + } + } + /* Some patches may have no cells at this point. These are either: + * (a) a closed manifold patch only incident on itself (sphere, torus, klein bottle, etc.). + * (b) an open manifold patch only incident on itself (has non-manifold boundaries). + * Make above and below cells for these patches. This will create a disconnected patch-cell + * bipartite graph, which will have to be fixed later. */ + for (int p : pinfo.index_range()) { + Patch &patch = pinfo.patch(p); + if (patch.cell_above == NO_INDEX) { + int c = cinfo.add_cell(); + patch.cell_above = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(p); + } + if (patch.cell_below == NO_INDEX) { + int c = cinfo.add_cell(); + patch.cell_below = c; + Cell &cell = cinfo.cell(c); + cell.add_patch(p); + } + } + if (dbg_level > 0) { + std::cout << "\nFIND_CELLS found " << cinfo.tot_cell() << " cells\nCells\n"; + for (int i : cinfo.index_range()) { + std::cout << i << ": " << cinfo.cell(i) << "\n"; + } + std::cout << "Patches\n"; + for (int i : pinfo.index_range()) { + std::cout << i << ": " << pinfo.patch(i) << "\n"; + } + if (dbg_level > 1) { + write_obj_cell_patch(tm, cinfo, pinfo, false, "postfindcells"); + } + } + return cinfo; +} + +/** + * Find the connected patch components (connects are via intermediate cells), and put + * component numbers in each patch. + * Return a Vector of components - each a Vector of the patch ids in the component. + */ +static Vector<Vector<int>> find_patch_components(const CellsInfo &cinfo, PatchesInfo &pinfo) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_PATCH_COMPONENTS\n"; + } + if (pinfo.tot_patch() == 0) { + return Vector<Vector<int>>(); + } + int current_component = 0; + Stack<int> stack; /* Patch indices to visit. */ + Vector<Vector<int>> ans; + for (int pstart : pinfo.index_range()) { + Patch &patch_pstart = pinfo.patch(pstart); + if (patch_pstart.component != NO_INDEX) { + continue; + } + ans.append(Vector<int>()); + ans[current_component].append(pstart); + stack.push(pstart); + patch_pstart.component = current_component; + while (!stack.is_empty()) { + int p = stack.pop(); + Patch &patch = pinfo.patch(p); + BLI_assert(patch.component == current_component); + for (int c : {patch.cell_above, patch.cell_below}) { + for (int pn : cinfo.cell(c).patches()) { + Patch &patch_neighbor = pinfo.patch(pn); + if (patch_neighbor.component == NO_INDEX) { + patch_neighbor.component = current_component; + stack.push(pn); + ans[current_component].append(pn); + } + } + } + } + ++current_component; + } + if (dbg_level > 0) { + std::cout << "found " << ans.size() << " components\n"; + for (int comp : ans.index_range()) { + std::cout << comp << ": " << ans[comp] << "\n"; + } + } + return ans; +} + +/** + * Do all patches have cell_above and cell_below set? + * Is the bipartite graph connected? + */ +static bool patch_cell_graph_ok(const CellsInfo &cinfo, const PatchesInfo &pinfo) +{ + for (int c : cinfo.index_range()) { + const Cell &cell = cinfo.cell(c); + if (cell.merged_to() != NO_INDEX) { + continue; + } + if (cell.patches().size() == 0) { + std::cout << "Patch/Cell graph disconnected at Cell " << c << " with no patches\n"; + return false; + } + for (int p : cell.patches()) { + if (p >= pinfo.tot_patch()) { + std::cout << "Patch/Cell graph has bad patch index at Cell " << c << "\n"; + return false; + } + } + } + for (int p : pinfo.index_range()) { + const Patch &patch = pinfo.patch(p); + if (patch.cell_above == NO_INDEX || patch.cell_below == NO_INDEX) { + std::cout << "Patch/Cell graph disconnected at Patch " << p + << " with one or two missing cells\n"; + return false; + } + if (patch.cell_above >= cinfo.tot_cell() || patch.cell_below >= cinfo.tot_cell()) { + std::cout << "Patch/Cell graph has bad cell index at Patch " << p << "\n"; + return false; + } + } + return true; +} + +/** + * Is trimesh tm PWN ("Piece-wise constant Winding Number")? + * See Zhou et al. paper for exact definition, but roughly + * means that the faces connect so as to form closed volumes. + * The actual definition says that if you calculate the + * generalized winding number of every point not exactly on + * the mesh, it will always be an integer. + * Necessary (but not sufficient) conditions that a mesh be PWN: + * No edges with a non-zero sum of incident face directions. + * I think that cases like Klein bottles are likely to satisfy + * this without being PWN. So this routine will be only + * approximately right. + */ +static bool is_pwn(const IMesh &tm, const TriMeshTopology &tmtopo) +{ + constexpr int dbg_level = 0; + for (auto item : tmtopo.edge_tri_map_items()) { + const Edge &edge = item.key; + int tot_orient = 0; + /* For each face t attached to edge, add +1 if the edge + * is positively in t, and -1 if negatively in t. */ + for (int t : *item.value) { + const Face &face = *tm.face(t); + BLI_assert(face.size() == 3); + for (int i : face.index_range()) { + if (face[i] == edge.v0()) { + if (face[(i + 1) % 3] == edge.v1()) { + ++tot_orient; + } + else { + BLI_assert(face[(i + 3 - 1) % 3] == edge.v1()); + --tot_orient; + } + } + } + } + if (tot_orient != 0) { + if (dbg_level > 0) { + std::cout << "edge causing non-pwn: " << edge << "\n"; + } + return false; + } + } + return true; +} + +/** + * Find which of the cells around edge e contains point p. + * Do this by inserting a dummy triangle containing v and sorting the + * triangles around the edge to find out where in the sort order + * the dummy triangle lies, then finding which cell is between + * the two triangles on either side of the dummy. + */ +static int find_cell_for_point_near_edge(mpq3 p, + const Edge &e, + const IMesh &tm, + const TriMeshTopology &tmtopo, + const PatchesInfo &pinfo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_CELL_FOR_POINT_NEAR_EDGE, p=" << p << " e=" << e << "\n"; + } + const Vector<int> *etris = tmtopo.edge_tris(e); + const Vert *dummy_vert = arena->add_or_find_vert(p, NO_INDEX); + const Face *dummy_tri = arena->add_face({e.v0(), e.v1(), dummy_vert}, + NO_INDEX, + {NO_INDEX, NO_INDEX, NO_INDEX}, + {false, false, false}); + BLI_assert(etris != nullptr); + Array<int> edge_tris(etris->size() + 1); + std::copy(etris->begin(), etris->end(), edge_tris.begin()); + edge_tris[edge_tris.size() - 1] = EXTRA_TRI_INDEX; + Array<int> sorted_tris = sort_tris_around_edge( + tm, tmtopo, e, edge_tris, edge_tris[0], dummy_tri); + if (dbg_level > 0) { + std::cout << "sorted tris = " << sorted_tris << "\n"; + } + int *p_sorted_dummy = std::find(sorted_tris.begin(), sorted_tris.end(), EXTRA_TRI_INDEX); + BLI_assert(p_sorted_dummy != sorted_tris.end()); + int dummy_index = p_sorted_dummy - sorted_tris.begin(); + int prev_tri = (dummy_index == 0) ? sorted_tris[sorted_tris.size() - 1] : + sorted_tris[dummy_index - 1]; + int next_tri = (dummy_index == sorted_tris.size() - 1) ? sorted_tris[0] : + sorted_tris[dummy_index + 1]; + if (dbg_level > 0) { + std::cout << "prev tri to dummy = " << prev_tri << "; next tri to dummy = " << next_tri + << "\n"; + } + const Patch &prev_patch = pinfo.patch(pinfo.tri_patch(prev_tri)); + if (dbg_level > 0) { + std::cout << "prev_patch = " << prev_patch << "\n"; + } + bool prev_flipped; + find_flap_vert(*tm.face(prev_tri), e, &prev_flipped); + int c = prev_flipped ? prev_patch.cell_below : prev_patch.cell_above; + if (dbg_level > 0) { + std::cout << "find_cell_for_point_near_edge returns " << c << "\n"; + } + return c; +} + +/** + * Find the ambient cell -- that is, the cell that is outside + * all other cells. + * If component_patches != nullptr, restrict consideration to patches + * in that vector. + * + * The method is to find an edge known to be on the convex hull + * of the mesh, then insert a dummy triangle that has that edge + * and a point known to be outside the whole mesh. Then sorting + * the triangles around the edge will reveal where the dummy triangle + * fits in that sorting order, and hence, the two adjacent patches + * to the dummy triangle - thus revealing the cell that the point + * known to be outside the whole mesh is in. + */ +static int find_ambient_cell(const IMesh &tm, + const Vector<int> *component_patches, + const TriMeshTopology &tmtopo, + const PatchesInfo &pinfo, + IMeshArena *arena) +{ + int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_AMBIENT_CELL\n"; + } + /* First find a vertex with the maximum x value. */ + /* Prefer not to populate the verts in the #IMesh just for this. */ + const Vert *v_extreme; + mpq_class extreme_x; + if (component_patches == nullptr) { + v_extreme = (*tm.face(0))[0]; + extreme_x = v_extreme->co_exact.x; + for (const Face *f : tm.faces()) { + for (const Vert *v : *f) { + const mpq_class &x = v->co_exact.x; + if (x > extreme_x) { + v_extreme = v; + extreme_x = x; + } + } + } + } + else { + if (dbg_level > 0) { + std::cout << "restrict to patches " << *component_patches << "\n"; + } + int p0 = (*component_patches)[0]; + v_extreme = (*tm.face(pinfo.patch(p0).tri(0)))[0]; + extreme_x = v_extreme->co_exact.x; + for (int p : *component_patches) { + for (int t : pinfo.patch(p).tris()) { + const Face *f = tm.face(t); + for (const Vert *v : *f) { + const mpq_class &x = v->co_exact.x; + if (x > extreme_x) { + v_extreme = v; + extreme_x = x; + } + } + } + } + } + if (dbg_level > 0) { + std::cout << "v_extreme = " << v_extreme << "\n"; + } + /* Find edge attached to v_extreme with max absolute slope + * when projected onto the XY plane. That edge is guaranteed to + * be on the convex hull of the mesh. */ + const Vector<Edge> &edges = tmtopo.vert_edges(v_extreme); + const mpq_class extreme_y = v_extreme->co_exact.y; + Edge ehull; + mpq_class max_abs_slope = -1; + for (Edge e : edges) { + const Vert *v_other = (e.v0() == v_extreme) ? e.v1() : e.v0(); + const mpq3 &co_other = v_other->co_exact; + mpq_class delta_x = co_other.x - extreme_x; + if (delta_x == 0) { + /* Vertical slope. */ + ehull = e; + break; + } + mpq_class abs_slope = abs((co_other.y - extreme_y) / delta_x); + if (abs_slope > max_abs_slope) { + ehull = e; + max_abs_slope = abs_slope; + } + } + if (dbg_level > 0) { + std::cout << "ehull = " << ehull << " slope = " << max_abs_slope << "\n"; + } + /* Sort triangles around ehull, including a dummy triangle that include a known point in ambient + * cell. */ + mpq3 p_in_ambient = v_extreme->co_exact; + p_in_ambient.x += 1; + int c_ambient = find_cell_for_point_near_edge(p_in_ambient, ehull, tm, tmtopo, pinfo, arena); + if (dbg_level > 0) { + std::cout << "FIND_AMBIENT_CELL returns " << c_ambient << "\n"; + } + return c_ambient; +} + +/** + * We need an edge on the convex hull of the edges incident on \a closestp + * in order to sort around, including a dummy triangle that has \a testp and + * the sorting edge vertices. So we don't want an edge that is co-linear + * with the line through \a testp and \a closestp. + * The method is to project onto a plane that contains `testp-closestp`, + * and then choose the edge that, when projected, has the maximum absolute + * slope (regarding the line `testp-closestp` as the x-axis for slope computation). + */ +static Edge find_good_sorting_edge(const Vert *testp, + const Vert *closestp, + const TriMeshTopology &tmtopo) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_GOOD_SORTING_EDGE testp = " << testp << ", closestp = " << closestp << "\n"; + } + /* We want to project the edges incident to closestp onto a plane + * whose ordinate direction will be regarded as going from closetp to testp, + * and whose abscissa direction is some perpendicular to that. + * A perpendicular direction can be found by swapping two coordinates + * and negating one, and zeroing out the third, being careful that one + * of the swapped vertices is non-zero. */ + const mpq3 &co_closest = closestp->co_exact; + const mpq3 &co_test = testp->co_exact; + BLI_assert(co_test != co_closest); + mpq3 abscissa = co_test - co_closest; + /* Find a non-zero-component axis of abscissa. */ + int axis; + for (axis = 0; axis < 3; ++axis) { + if (abscissa[axis] != 0) { + break; + } + } + BLI_assert(axis < 3); + int axis_next = (axis + 1) % 3; + int axis_next_next = (axis_next + 1) % 3; + mpq3 ordinate; + ordinate[axis] = abscissa[axis_next]; + ordinate[axis_next] = -abscissa[axis]; + ordinate[axis_next_next] = 0; + /* By construction, dot(abscissa, ordinate) == 0, so they are perpendicular. */ + mpq3 normal = mpq3::cross(abscissa, ordinate); + if (dbg_level > 0) { + std::cout << "abscissa = " << abscissa << "\n"; + std::cout << "ordinate = " << ordinate << "\n"; + std::cout << "normal = " << normal << "\n"; + } + mpq_class nlen2 = normal.length_squared(); + mpq_class max_abs_slope = -1; + Edge esort; + const Vector<Edge> &edges = tmtopo.vert_edges(closestp); + for (Edge e : edges) { + const Vert *v_other = (e.v0() == closestp) ? e.v1() : e.v0(); + const mpq3 &co_other = v_other->co_exact; + mpq3 evec = co_other - co_closest; + /* Get projection of evec onto plane of abscissa and ordinate. */ + mpq3 proj_evec = evec - (mpq3::dot(evec, normal) / nlen2) * normal; + /* The projection calculations along the abscissa and ordinate should + * be scaled by 1/abscissa and 1/ordinate respectively, + * but we can skip: it won't affect which `evec` has the maximum slope. */ + mpq_class evec_a = mpq3::dot(proj_evec, abscissa); + mpq_class evec_o = mpq3::dot(proj_evec, ordinate); + if (dbg_level > 0) { + std::cout << "e = " << e << "\n"; + std::cout << "v_other = " << v_other << "\n"; + std::cout << "evec = " << evec << ", proj_evec = " << proj_evec << "\n"; + std::cout << "evec_a = " << evec_a << ", evec_o = " << evec_o << "\n"; + } + if (evec_a == 0) { + /* evec is perpendicular to abscissa. */ + esort = e; + if (dbg_level > 0) { + std::cout << "perpendicular esort is " << esort << "\n"; + } + break; + } + mpq_class abs_slope = abs(evec_o / evec_a); + if (abs_slope > max_abs_slope) { + esort = e; + max_abs_slope = abs_slope; + if (dbg_level > 0) { + std::cout << "with abs_slope " << abs_slope << " new esort is " << esort << "\n"; + } + } + } + return esort; +} + +/** + * Find the cell that contains v. Consider the cells adjacent to triangle t. + * The close_edge and close_vert values are what were returned by + * #closest_on_tri_to_point when determining that v was close to t. + * They will indicate whether the point of closest approach to t is to + * an edge of t, a vertex of t, or somewhere inside t. + * + * 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. + */ +static int find_containing_cell(const Vert *v, + int t, + int close_edge, + int close_vert, + const PatchesInfo &pinfo, + const IMesh &tm, + const TriMeshTopology &tmtopo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_CONTAINING_CELL v=" << v << ", t=" << t << "\n"; + } + const Face &tri = *tm.face(t); + Edge etest; + if (close_edge == -1 && close_vert == -1) { + /* Choose any edge if closest point is inside the triangle. */ + close_edge = 0; + } + if (close_edge != -1) { + const Vert *v0 = tri[close_edge]; + const Vert *v1 = tri[(close_edge + 1) % 3]; + const Vector<Edge> &edges = tmtopo.vert_edges(v0); + if (dbg_level > 0) { + std::cout << "look for edge containing " << v0 << " and " << v1 << "\n"; + std::cout << " in edges: "; + for (Edge e : edges) { + std::cout << e << " "; + } + std::cout << "\n"; + } + for (Edge e : edges) { + if ((e.v0() == v0 && e.v1() == v1) || (e.v0() == v1 && e.v1() == v0)) { + etest = e; + break; + } + } + } + else { + int cv = close_vert; + const Vert *vert_cv = tri[cv]; + if (vert_cv == v) { + /* Need to use another one to find sorting edge. */ + vert_cv = tri[(cv + 1) % 3]; + BLI_assert(vert_cv != v); + } + etest = find_good_sorting_edge(v, vert_cv, tmtopo); + } + BLI_assert(etest.v0() != nullptr); + if (dbg_level > 0) { + std::cout << "etest = " << etest << "\n"; + } + int c = find_cell_for_point_near_edge(v->co_exact, etest, tm, tmtopo, pinfo, arena); + if (dbg_level > 0) { + std::cout << "find_containing_cell returns " << c << "\n"; + } + return c; +} + +/** + * Find the closest point in triangle (a, b, c) to point p. + * Return the distance squared to that point. + * Also, if the closest point in the triangle is on a vertex, + * return 0, 1, or 2 for a, b, c in *r_vert; else -1. + * If the closest point is on an edge, return 0, 1, or 2 + * for edges ab, bc, or ca in *r_edge; else -1. + * (Adapted from #closest_on_tri_to_point_v3()). + */ +static mpq_class closest_on_tri_to_point( + const mpq3 &p, const mpq3 &a, const mpq3 &b, const mpq3 &c, int *r_edge, int *r_vert) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "CLOSEST_ON_TRI_TO_POINT p = " << p << "\n"; + std::cout << " a = " << a << ", b = " << b << ", c = " << c << "\n"; + } + /* Check if p in vertex region outside a. */ + mpq3 ab = b - a; + mpq3 ac = c - a; + mpq3 ap = p - a; + mpq_class d1 = mpq3::dot(ab, ap); + mpq_class d2 = mpq3::dot(ac, ap); + if (d1 <= 0 && d2 <= 0) { + /* Barycentric coordinates (1,0,0). */ + *r_edge = -1; + *r_vert = 0; + if (dbg_level > 0) { + std::cout << " answer = a\n"; + } + return mpq3::distance_squared(p, a); + } + /* Check if p in vertex region outside b. */ + mpq3 bp = p - b; + mpq_class d3 = mpq3::dot(ab, bp); + mpq_class d4 = mpq3::dot(ac, bp); + if (d3 >= 0 && d4 <= d3) { + /* Barycentric coordinates (0,1,0). */ + *r_edge = -1; + *r_vert = 1; + if (dbg_level > 0) { + std::cout << " answer = b\n"; + } + return mpq3::distance_squared(p, b); + } + /* Check if p in region of ab. */ + mpq_class vc = d1 * d4 - d3 * d2; + if (vc <= 0 && d1 >= 0 && d3 <= 0) { + mpq_class v = d1 / (d1 - d3); + /* Barycentric coordinates (1-v,v,0). */ + mpq3 r = a + v * ab; + *r_vert = -1; + *r_edge = 0; + if (dbg_level > 0) { + std::cout << " answer = on ab at " << r << "\n"; + } + return mpq3::distance_squared(p, r); + } + /* Check if p in vertex region outside c. */ + mpq3 cp = p - c; + mpq_class d5 = mpq3::dot(ab, cp); + mpq_class d6 = mpq3::dot(ac, cp); + if (d6 >= 0 && d5 <= d6) { + /* Barycentric coordinates (0,0,1). */ + *r_edge = -1; + *r_vert = 2; + if (dbg_level > 0) { + std::cout << " answer = c\n"; + } + return mpq3::distance_squared(p, c); + } + /* Check if p in edge region of ac. */ + mpq_class vb = d5 * d2 - d1 * d6; + if (vb <= 0 && d2 >= 0 && d6 <= 0) { + mpq_class w = d2 / (d2 - d6); + /* Barycentric coordinates (1-w,0,w). */ + mpq3 r = a + w * ac; + *r_vert = -1; + *r_edge = 2; + if (dbg_level > 0) { + std::cout << " answer = on ac at " << r << "\n"; + } + return mpq3::distance_squared(p, r); + } + /* Check if p in edge region of bc. */ + mpq_class va = d3 * d6 - d5 * d4; + if (va <= 0 && (d4 - d3) >= 0 && (d5 - d6) >= 0) { + mpq_class w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + /* Barycentric coordinates (0,1-w,w). */ + mpq3 r = c - b; + r = w * r; + r = r + b; + *r_vert = -1; + *r_edge = 1; + if (dbg_level > 0) { + std::cout << " answer = on bc at " << r << "\n"; + } + return mpq3::distance_squared(p, r); + } + /* p inside face region. Compute barycentric coordinates (u,v,w). */ + mpq_class denom = 1 / (va + vb + vc); + mpq_class v = vb * denom; + mpq_class w = vc * denom; + ac = w * ac; + mpq3 r = a + v * ab; + r = r + ac; + *r_vert = -1; + *r_edge = -1; + if (dbg_level > 0) { + std::cout << " answer = inside at " << r << "\n"; + } + return mpq3::distance_squared(p, r); +} + +struct ComponentContainer { + int containing_component{NO_INDEX}; + int nearest_cell{NO_INDEX}; + mpq_class dist_to_cell; + + ComponentContainer(int cc, int cell, mpq_class d) + : containing_component(cc), nearest_cell(cell), dist_to_cell(d) + { + } +}; + +/** + * Find out all the components, not equal to comp, that contain a point + * in comp in a non-ambient cell of those components. + * In other words, find the components that comp is nested inside + * (maybe not directly nested, which is why there can be more than one). + */ +static Vector<ComponentContainer> find_component_containers(int comp, + const Vector<Vector<int>> &components, + const Array<int> &ambient_cell, + const IMesh &tm, + const PatchesInfo &pinfo, + const TriMeshTopology &tmtopo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_COMPONENT_CONTAINERS for comp " << comp << "\n"; + } + Vector<ComponentContainer> ans; + int test_p = components[comp][0]; + int test_t = pinfo.patch(test_p).tri(0); + const Vert *test_v = tm.face(test_t)[0].vert[0]; + if (dbg_level > 0) { + std::cout << "test vertex in comp: " << test_v << "\n"; + } + for (int comp_other : components.index_range()) { + if (comp == comp_other) { + continue; + } + if (dbg_level > 0) { + std::cout << "comp_other = " << comp_other << "\n"; + } + int nearest_tri = NO_INDEX; + int nearest_tri_close_vert = -1; + int nearest_tri_close_edge = -1; + mpq_class nearest_tri_dist_squared; + for (int p : components[comp_other]) { + const Patch &patch = pinfo.patch(p); + for (int t : patch.tris()) { + const Face &tri = *tm.face(t); + if (dbg_level > 1) { + std::cout << "tri " << t << " = " << &tri << "\n"; + } + int close_vert; + int close_edge; + mpq_class d2 = closest_on_tri_to_point(test_v->co_exact, + tri[0]->co_exact, + tri[1]->co_exact, + tri[2]->co_exact, + &close_edge, + &close_vert); + if (dbg_level > 1) { + std::cout << " close_edge=" << close_edge << " close_vert=" << close_vert + << " dsquared=" << d2.get_d() << "\n"; + } + if (nearest_tri == NO_INDEX || d2 < nearest_tri_dist_squared) { + nearest_tri = t; + nearest_tri_close_edge = close_edge; + nearest_tri_close_vert = close_vert; + nearest_tri_dist_squared = d2; + } + } + } + if (dbg_level > 0) { + std::cout << "closest tri to comp=" << comp << " in comp_other=" << comp_other << " is t" + << nearest_tri << "\n"; + const Face *tn = tm.face(nearest_tri); + std::cout << "tri = " << tn << "\n"; + std::cout << " (" << tn->vert[0]->co << "," << tn->vert[1]->co << "," << tn->vert[2]->co + << ")\n"; + } + int containing_cell = find_containing_cell(test_v, + nearest_tri, + nearest_tri_close_edge, + nearest_tri_close_vert, + + pinfo, + tm, + tmtopo, + arena); + if (dbg_level > 0) { + std::cout << "containing cell = " << containing_cell << "\n"; + } + if (containing_cell != ambient_cell[comp_other]) { + ans.append(ComponentContainer(comp_other, containing_cell, nearest_tri_dist_squared)); + } + } + return ans; +} + +/** + * The cells and patches are supposed to form a bipartite graph. + * The graph may be disconnected (if parts of meshes are nested or side-by-side + * without intersection with other each other). + * Connect the bipartite graph. This involves discovering the connected components + * of the patches, then the nesting structure of those components. + */ +static void finish_patch_cell_graph(const IMesh &tm, + CellsInfo &cinfo, + PatchesInfo &pinfo, + const TriMeshTopology &tmtopo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FINISH_PATCH_CELL_GRAPH\n"; + } + Vector<Vector<int>> components = find_patch_components(cinfo, pinfo); + if (components.size() <= 1) { + if (dbg_level > 0) { + std::cout << "one component so finish_patch_cell_graph does no work\n"; + } + return; + } + if (dbg_level > 0) { + std::cout << "components:\n"; + for (int comp : components.index_range()) { + std::cout << comp << ": " << components[comp] << "\n"; + } + } + Array<int> ambient_cell(components.size()); + for (int comp : components.index_range()) { + ambient_cell[comp] = find_ambient_cell(tm, &components[comp], tmtopo, pinfo, arena); + } + if (dbg_level > 0) { + std::cout << "ambient cells:\n"; + for (int comp : ambient_cell.index_range()) { + std::cout << comp << ": " << ambient_cell[comp] << "\n"; + } + } + int tot_components = components.size(); + Array<Vector<ComponentContainer>> comp_cont(tot_components); + for (int comp : components.index_range()) { + comp_cont[comp] = find_component_containers( + comp, components, ambient_cell, tm, pinfo, tmtopo, arena); + } + if (dbg_level > 0) { + std::cout << "component containers:\n"; + for (int comp : comp_cont.index_range()) { + std::cout << comp << ": "; + for (const ComponentContainer &cc : comp_cont[comp]) { + std::cout << "[containing_comp=" << cc.containing_component + << ", nearest_cell=" << cc.nearest_cell << ", d2=" << cc.dist_to_cell << "] "; + } + std::cout << "\n"; + } + } + if (dbg_level > 1) { + write_obj_cell_patch(tm, cinfo, pinfo, false, "beforemerge"); + } + /* For nested components, merge their ambient cell with the nearest containing cell. */ + Vector<int> outer_components; + for (int comp : comp_cont.index_range()) { + if (comp_cont[comp].size() == 0) { + outer_components.append(comp); + } + else { + ComponentContainer &closest = comp_cont[comp][0]; + for (int i = 1; i < comp_cont[comp].size(); ++i) { + if (comp_cont[comp][i].dist_to_cell < closest.dist_to_cell) { + closest = comp_cont[comp][i]; + } + } + int comp_ambient = ambient_cell[comp]; + int cont_cell = closest.nearest_cell; + if (dbg_level > 0) { + std::cout << "merge comp " << comp << "'s ambient cell=" << comp_ambient << " to cell " + << cont_cell << "\n"; + } + merge_cells(cont_cell, comp_ambient, cinfo, pinfo); + } + } + /* For outer components (not nested in any other component), merge their ambient cells. */ + if (outer_components.size() > 1) { + int merged_ambient = ambient_cell[outer_components[0]]; + for (int i = 1; i < outer_components.size(); ++i) { + if (dbg_level > 0) { + std::cout << "merge comp " << outer_components[i] + << "'s ambient cell=" << ambient_cell[outer_components[i]] << " to cell " + << merged_ambient << "\n"; + } + merge_cells(merged_ambient, ambient_cell[outer_components[i]], cinfo, pinfo); + } + } + if (dbg_level > 0) { + std::cout << "after FINISH_PATCH_CELL_GRAPH\nCells\n"; + for (int i : cinfo.index_range()) { + if (cinfo.cell(i).merged_to() == NO_INDEX) { + std::cout << i << ": " << cinfo.cell(i) << "\n"; + } + } + std::cout << "Patches\n"; + for (int i : pinfo.index_range()) { + std::cout << i << ": " << pinfo.patch(i) << "\n"; + } + if (dbg_level > 1) { + write_obj_cell_patch(tm, cinfo, pinfo, false, "finished"); + } + } +} + +/** + * Starting with ambient cell c_ambient, with all zeros for winding numbers, + * propagate winding numbers to all the other cells. + * There will be a vector of \a nshapes winding numbers in each cell, one per + * input shape. + * As one crosses a patch into a new cell, the original shape (mesh part) + * that that patch was part of dictates which winding number changes. + * The shape_fn(triangle_number) function should return the shape that the + * triangle is part of. + * Also, as soon as the winding numbers for a cell are set, use bool_optype + * to decide whether that cell is included or excluded from the boolean output. + * If included, the cell's in_output_volume will be set to true. + */ +static void propagate_windings_and_in_output_volume(PatchesInfo &pinfo, + CellsInfo &cinfo, + int c_ambient, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn) +{ + int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "PROPAGATE_WINDINGS, ambient cell = " << c_ambient << "\n"; + } + Cell &cell_ambient = cinfo.cell(c_ambient); + cell_ambient.seed_ambient_winding(); + /* Use a vector as a queue. It can't grow bigger than number of cells. */ + Vector<int> queue; + queue.reserve(cinfo.tot_cell()); + int queue_head = 0; + queue.append(c_ambient); + while (queue_head < queue.size()) { + int c = queue[queue_head++]; + if (dbg_level > 1) { + std::cout << "process cell " << c << "\n"; + } + Cell &cell = cinfo.cell(c); + for (int p : cell.patches()) { + Patch &patch = pinfo.patch(p); + bool p_above_c = patch.cell_below == c; + int c_neighbor = p_above_c ? patch.cell_above : patch.cell_below; + if (dbg_level > 1) { + std::cout << " patch " << p << " p_above_c = " << p_above_c << "\n"; + std::cout << " c_neighbor = " << c_neighbor << "\n"; + } + Cell &cell_neighbor = cinfo.cell(c_neighbor); + if (!cell_neighbor.winding_assigned()) { + int winding_delta = p_above_c ? -1 : 1; + int t = patch.tri(0); + int shape = shape_fn(t); + BLI_assert(shape < nshapes); + UNUSED_VARS_NDEBUG(nshapes); + if (dbg_level > 1) { + std::cout << " representative tri " << t << ": in shape " << shape << "\n"; + } + cell_neighbor.set_winding_and_in_output_volume(cell, shape, winding_delta, op); + if (dbg_level > 1) { + std::cout << " now cell_neighbor = " << cell_neighbor << "\n"; + } + queue.append(c_neighbor); + BLI_assert(queue.size() <= cinfo.tot_cell()); + } + } + } + if (dbg_level > 0) { + std::cout << "\nPROPAGATE_WINDINGS result\n"; + for (int i = 0; i < cinfo.tot_cell(); ++i) { + std::cout << i << ": " << cinfo.cell(i) << "\n"; + } + } +} + +/** + * Given an array of winding numbers, where the ith entry is a cell's winding + * number with respect to input shape (mesh part) i, return true if the + * cell should be included in the output of the boolean operation. + * Intersection: all the winding numbers must be nonzero. + * Union: at least one winding number must be nonzero. + * Difference (first shape minus the rest): first winding number must be nonzero + * and the rest must have at least one zero winding number. + */ +static bool apply_bool_op(BoolOpType bool_optype, const Array<int> &winding) +{ + int nw = winding.size(); + BLI_assert(nw > 0); + switch (bool_optype) { + case BoolOpType::Intersect: { + for (int i = 0; i < nw; ++i) { + if (winding[i] == 0) { + return false; + } + } + return true; + } + case BoolOpType::Union: { + for (int i = 0; i < nw; ++i) { + if (winding[i] != 0) { + return true; + } + } + return false; + } + case BoolOpType::Difference: { + /* if nw > 2, make it shape 0 minus the union of the rest. */ + if (winding[0] == 0) { + return false; + } + if (nw == 1) { + return true; + } + for (int i = 1; i < nw; ++i) { + if (winding[i] == 0) { + return true; + } + } + return false; + } + default: + return false; + } +} + +/** + * Special processing for extract_from_in_output_volume_diffs to handle + * triangles that are part of stacks of geometrically identical + * triangles enclosing zero volume cells. + */ +static void extract_zero_volume_cell_tris(Vector<Face *> &r_tris, + const IMesh &tm_subdivided, + const PatchesInfo &pinfo, + const CellsInfo &cinfo, + IMeshArena *arena) +{ + int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "extract_zero_volume_cell_tris\n"; + } + /* Find patches that are adjacent to zero-volume cells. */ + Array<bool> adj_to_zv(pinfo.tot_patch()); + for (int p : pinfo.index_range()) { + const Patch &patch = pinfo.patch(p); + if (cinfo.cell(patch.cell_above).zero_volume() || cinfo.cell(patch.cell_below).zero_volume()) { + adj_to_zv[p] = true; + } + else { + adj_to_zv[p] = false; + } + } + /* Partition the adj_to_zv patches into stacks. */ + Vector<Vector<int>> patch_stacks; + Array<bool> allocated_to_stack(pinfo.tot_patch(), false); + for (int p : pinfo.index_range()) { + if (!adj_to_zv[p] || allocated_to_stack[p]) { + continue; + } + int stack_index = patch_stacks.size(); + patch_stacks.append(Vector<int>{p}); + Vector<int> &stack = patch_stacks[stack_index]; + Vector<bool> flipped{false}; + allocated_to_stack[p] = true; + /* We arbitrarily choose p's above and below directions as above and below for whole stack. + * Triangles in the stack that don't follow that convention are marked with flipped = true. + * The non-zero-volume cell above the whole stack, following this convention, is + * above_stack_cell. The non-zero-volume cell below the whole stack is #below_stack_cell. */ + /* First, walk in the above_cell direction from p. */ + int pwalk = p; + const Patch *pwalk_patch = &pinfo.patch(pwalk); + int c = pwalk_patch->cell_above; + const Cell *cell = &cinfo.cell(c); + while (cell->zero_volume()) { + /* In zero-volume cells, the cell should have exactly two patches. */ + BLI_assert(cell->patches().size() == 2); + int pother = cell->patches()[0] == pwalk ? cell->patches()[1] : cell->patches()[0]; + bool flip = pinfo.patch(pother).cell_above == c; + flipped.append(flip); + stack.append(pother); + allocated_to_stack[pother] = true; + pwalk = pother; + pwalk_patch = &pinfo.patch(pwalk); + c = flip ? pwalk_patch->cell_below : pwalk_patch->cell_above; + cell = &cinfo.cell(c); + } + const Cell *above_stack_cell = cell; + /* Now walk in the below_cell direction from p. */ + pwalk = p; + pwalk_patch = &pinfo.patch(pwalk); + c = pwalk_patch->cell_below; + cell = &cinfo.cell(c); + while (cell->zero_volume()) { + BLI_assert(cell->patches().size() == 2); + int pother = cell->patches()[0] == pwalk ? cell->patches()[1] : cell->patches()[0]; + bool flip = pinfo.patch(pother).cell_below == c; + flipped.append(flip); + stack.append(pother); + allocated_to_stack[pother] = true; + pwalk = pother; + pwalk_patch = &pinfo.patch(pwalk); + c = flip ? pwalk_patch->cell_above : pwalk_patch->cell_below; + cell = &cinfo.cell(c); + } + const Cell *below_stack_cell = cell; + if (dbg_level > 0) { + std::cout << "process zero-volume patch stack " << stack << "\n"; + std::cout << "flipped = "; + for (bool b : flipped) { + std::cout << b << " "; + } + std::cout << "\n"; + } + if (above_stack_cell->in_output_volume() ^ below_stack_cell->in_output_volume()) { + bool need_flipped_tri = above_stack_cell->in_output_volume(); + if (dbg_level > 0) { + std::cout << "need tri " << (need_flipped_tri ? "flipped" : "") << "\n"; + } + int t_to_add = NO_INDEX; + for (int i : stack.index_range()) { + if (flipped[i] == need_flipped_tri) { + t_to_add = pinfo.patch(stack[i]).tri(0); + if (dbg_level > 0) { + std::cout << "using tri " << t_to_add << "\n"; + } + r_tris.append(tm_subdivided.face(t_to_add)); + break; + } + } + if (t_to_add == NO_INDEX) { + const Face *f = tm_subdivided.face(pinfo.patch(p).tri(0)); + const Face &tri = *f; + /* We need flipped version or else we would have found it above. */ + std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]}; + std::array<int, 3> flipped_e_origs = { + tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]}; + std::array<bool, 3> flipped_is_intersect = { + tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]}; + Face *flipped_f = arena->add_face( + flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect); + r_tris.append(flipped_f); + } + } + } +} + +/** + * Extract the output mesh from tm_subdivided and return it as a new mesh. + * The cells in \a cinfo must have cells-to-be-retained with in_output_volume set. + * We keep only triangles between those in the output volume and those not in. + * We flip the normals of any triangle that has an in_output_volume cell above + * and a not-in_output_volume cell below. + * For all stacks of exact duplicate co-planar triangles, we want to + * include either one version of the triangle or none, depending on + * whether the in_output_volume in_output_volumes on either side of the stack are + * different or the same. + */ +static IMesh extract_from_in_output_volume_diffs(const IMesh &tm_subdivided, + const PatchesInfo &pinfo, + const CellsInfo &cinfo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nEXTRACT_FROM_FLAG_DIFFS\n"; + } + Vector<Face *> out_tris; + out_tris.reserve(tm_subdivided.face_size()); + bool any_zero_volume_cell = false; + for (int t : tm_subdivided.face_index_range()) { + int p = pinfo.tri_patch(t); + const Patch &patch = pinfo.patch(p); + const Cell &cell_above = cinfo.cell(patch.cell_above); + const Cell &cell_below = cinfo.cell(patch.cell_below); + if (dbg_level > 0) { + std::cout << "tri " << t << ": cell_above=" << patch.cell_above + << " cell_below=" << patch.cell_below << "\n"; + std::cout << " in_output_volume_above=" << cell_above.in_output_volume() + << " in_output_volume_below=" << cell_below.in_output_volume() << "\n"; + } + bool adjacent_zero_volume_cell = cell_above.zero_volume() || cell_below.zero_volume(); + any_zero_volume_cell |= adjacent_zero_volume_cell; + if (cell_above.in_output_volume() ^ cell_below.in_output_volume() && + !adjacent_zero_volume_cell) { + bool flip = cell_above.in_output_volume(); + if (dbg_level > 0) { + std::cout << "need tri " << t << " flip=" << flip << "\n"; + } + Face *f = tm_subdivided.face(t); + if (flip) { + Face &tri = *f; + std::array<const Vert *, 3> flipped_vs = {tri[0], tri[2], tri[1]}; + std::array<int, 3> flipped_e_origs = { + tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]}; + std::array<bool, 3> flipped_is_intersect = { + tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]}; + Face *flipped_f = arena->add_face( + flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect); + out_tris.append(flipped_f); + } + else { + out_tris.append(f); + } + } + } + if (any_zero_volume_cell) { + extract_zero_volume_cell_tris(out_tris, tm_subdivided, pinfo, cinfo, arena); + } + return IMesh(out_tris); +} + +static const char *bool_optype_name(BoolOpType op) +{ + switch (op) { + case BoolOpType::None: + return "none"; + case BoolOpType::Intersect: + return "intersect"; + case BoolOpType::Union: + return "union"; + case BoolOpType::Difference: + return "difference"; + default: + return "<unknown>"; + } +} + +static mpq3 calc_point_inside_tri(const Face &tri) +{ + const Vert *v0 = tri.vert[0]; + const Vert *v1 = tri.vert[1]; + const Vert *v2 = tri.vert[2]; + mpq3 ans = v0->co_exact / 3 + v1->co_exact / 3 + v2->co_exact / 3; + return ans; +} + +/** + * Return the Generalized Winding Number of point \a testp with respect to the + * volume implied by the faces for which shape_fn returns the value shape. + * See "Robust Inside-Outside Segmentation using Generalized Winding Numbers" + * by Jacobson, Kavan, and Sorkine-Hornung. + * This is like a winding number in that if it is positive, the point + * is inside the volume. But it is tolerant of not-completely-watertight + * volumes, still doing a passable job of classifying inside/outside + * as we intuitively understand that to mean. + * + * TOOD: speed up this calculation using the hierarchical algorithm in that paper. + */ +static double generalized_winding_number(const IMesh &tm, + std::function<int(int)> shape_fn, + const double3 &testp, + int shape) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "GENERALIZED_WINDING_NUMBER testp = " << testp << ", shape = " << shape << "\n"; + } + double gwn = 0; + for (int t : tm.face_index_range()) { + const Face *f = tm.face(t); + const Face &tri = *f; + if (shape_fn(tri.orig) == shape) { + if (dbg_level > 0) { + std::cout << "accumulate for tri t = " << t << " = " << f << "\n"; + } + const Vert *v0 = tri.vert[0]; + const Vert *v1 = tri.vert[1]; + const Vert *v2 = tri.vert[2]; + double3 a = v0->co - testp; + double3 b = v1->co - testp; + double3 c = v2->co - testp; + /* Calculate the solid angle of abc relative to origin. + * See "The Solid Angle of a Plane Triangle" by Oosterom and Strackee + * for the derivation of the formula. */ + double alen = a.length(); + double blen = b.length(); + double clen = c.length(); + double3 bxc = double3::cross_high_precision(b, c); + double num = double3::dot(a, bxc); + double denom = alen * blen * clen + double3::dot(a, b) * clen + double3::dot(a, c) * blen + + double3::dot(b, c) * alen; + if (denom == 0.0) { + if (dbg_level > 0) { + std::cout << "denom == 0, skipping this tri\n"; + } + continue; + } + double x = atan2(num, denom); + double fgwn = 2.0 * x; + if (dbg_level > 0) { + std::cout << "tri contributes " << fgwn << "\n"; + } + gwn += fgwn; + } + } + gwn = gwn / (M_PI * 4.0); + if (dbg_level > 0) { + std::cout << "final gwn = " << gwn << "\n"; + } + return gwn; +} + +/** + * Return true if point \a testp is inside the volume implied by the + * faces for which the shape_fn returns the value shape. + */ +static bool point_is_inside_shape(const IMesh &tm, + std::function<int(int)> shape_fn, + const double3 &testp, + int shape) +{ + double gwn = generalized_winding_number(tm, shape_fn, testp, shape); + /* Due to floating point error, an outside point should get a value + * of zero for gwn, but may have a very slightly positive value instead. + * It is not important to get this epsilon very small, because practical + * cases of interest will have gwn at least 0.2 if it is not zero. */ + return (gwn > 0.01); +} + +/** + * Use the Generalized Winding Number method for deciding if a patch of the + * mesh is supposed to be included or excluded in the boolean result, + * and return the mesh that is the boolean result. + */ +static IMesh gwn_boolean(const IMesh &tm, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + const PatchesInfo &pinfo, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "GWN_BOOLEAN\n"; + } + IMesh ans; + Vector<Face *> out_faces; + out_faces.reserve(tm.face_size()); + BLI_assert(nshapes == 2); /* TODO: generalize. */ + UNUSED_VARS_NDEBUG(nshapes); + for (int p : pinfo.index_range()) { + const Patch &patch = pinfo.patch(p); + /* For test triangle, choose one in the middle of patch list + * as the ones near the beginning may be very near other patches. */ + int test_t_index = patch.tri(patch.tot_tri() / 2); + Face &tri_test = *tm.face(test_t_index); + /* Assume all triangles in a patch are in the same shape. */ + int shape = shape_fn(tri_test.orig); + if (dbg_level > 0) { + std::cout << "process patch " << p << " = " << patch << "\n"; + std::cout << "test tri = " << test_t_index << " = " << &tri_test << "\n"; + std::cout << "shape = " << shape << "\n"; + } + if (shape == -1) { + continue; + } + mpq3 test_point = calc_point_inside_tri(tri_test); + double3 test_point_db(test_point[0].get_d(), test_point[1].get_d(), test_point[2].get_d()); + if (dbg_level > 0) { + std::cout << "test point = " << test_point_db << "\n"; + } + int other_shape = 1 - shape; + bool inside = point_is_inside_shape(tm, shape_fn, test_point_db, other_shape); + if (dbg_level > 0) { + std::cout << "test point is " << (inside ? "inside\n" : "outside\n"); + } + bool do_remove; + bool do_flip; + switch (op) { + case BoolOpType::Intersect: + do_remove = !inside; + do_flip = false; + break; + case BoolOpType::Union: + do_remove = inside; + do_flip = false; + break; + case BoolOpType::Difference: + do_remove = (shape == 0) ? inside : !inside; + do_flip = (shape == 1); + break; + default: + do_remove = false; + do_flip = false; + BLI_assert(false); + } + if (dbg_level > 0) { + std::cout << "result for patch " << p << ": remove=" << do_remove << ", flip=" << do_flip + << "\n"; + } + if (!do_remove) { + for (int t : patch.tris()) { + Face *f = tm.face(t); + if (!do_flip) { + out_faces.append(f); + } + else { + Face &tri = *f; + /* We need flipped version of f. */ + Array<const Vert *> flipped_vs = {tri[0], tri[2], tri[1]}; + Array<int> flipped_e_origs = {tri.edge_orig[2], tri.edge_orig[1], tri.edge_orig[0]}; + Array<bool> flipped_is_intersect = { + tri.is_intersect[2], tri.is_intersect[1], tri.is_intersect[0]}; + Face *flipped_f = arena->add_face( + flipped_vs, f->orig, flipped_e_origs, flipped_is_intersect); + out_faces.append(flipped_f); + } + } + } + } + ans.set_faces(out_faces); + return ans; +} + +/** + * Which CDT output edge index is for an edge between output verts + * v1 and v2 (in either order)? + * \return -1 if none. + */ +static int find_cdt_edge(const CDT_result<mpq_class> &cdt_out, int v1, int v2) +{ + for (int e : cdt_out.edge.index_range()) { + const std::pair<int, int> &edge = cdt_out.edge[e]; + if ((edge.first == v1 && edge.second == v2) || (edge.first == v2 && edge.second == v1)) { + return e; + } + } + return -1; +} + +/** + * Tessellate face f into triangles and return an array of `const Face *` + * giving that triangulation. + * Care is taken so that the original edge index associated with + * each edge in the output triangles either matches the original edge + * for the (identical) edge of f, or else is -1. So diagonals added + * for triangulation can later be identified by having #NO_INDEX for original. + */ +static Array<Face *> triangulate_poly(Face *f, IMeshArena *arena) +{ + int flen = f->size(); + CDT_input<mpq_class> cdt_in; + cdt_in.vert = Array<mpq2>(flen); + cdt_in.face = Array<Vector<int>>(1); + cdt_in.face[0].reserve(flen); + for (int i : f->index_range()) { + cdt_in.face[0].append(i); + } + /* Project poly along dominant axis of normal to get 2d coords. */ + if (!f->plane_populated()) { + f->populate_plane(false); + } + const double3 &poly_normal = f->plane->norm; + int axis = double3::dominant_axis(poly_normal); + /* If project down y axis as opposed to x or z, the orientation + * of the polygon will be reversed. + * Yet another reversal happens if the poly normal in the dominant + * direction is opposite that of the positive dominant axis. */ + bool rev1 = (axis == 1); + bool rev2 = poly_normal[axis] < 0; + bool rev = rev1 ^ rev2; + for (int i = 0; i < flen; ++i) { + int ii = rev ? flen - i - 1 : i; + mpq2 &p2d = cdt_in.vert[ii]; + int k = 0; + for (int j = 0; j < 3; ++j) { + if (j != axis) { + p2d[k++] = (*f)[ii]->co_exact[j]; + } + } + } + CDT_result<mpq_class> cdt_out = delaunay_2d_calc(cdt_in, CDT_INSIDE); + int n_tris = cdt_out.face.size(); + Array<Face *> ans(n_tris); + for (int t = 0; t < n_tris; ++t) { + int i_v_out[3]; + const Vert *v[3]; + int eo[3]; + for (int i = 0; i < 3; ++i) { + i_v_out[i] = cdt_out.face[t][i]; + v[i] = (*f)[cdt_out.vert_orig[i_v_out[i]][0]]; + } + for (int i = 0; i < 3; ++i) { + int e_out = find_cdt_edge(cdt_out, i_v_out[i], i_v_out[(i + 1) % 3]); + BLI_assert(e_out != -1); + eo[i] = NO_INDEX; + for (int orig : cdt_out.edge_orig[e_out]) { + if (orig != NO_INDEX) { + eo[i] = orig; + break; + } + } + } + if (rev) { + ans[t] = arena->add_face( + {v[0], v[2], v[1]}, f->orig, {eo[2], eo[1], eo[0]}, {false, false, false}); + } + else { + ans[t] = arena->add_face( + {v[0], v[1], v[2]}, f->orig, {eo[0], eo[1], eo[2]}, {false, false, false}); + } + } + return ans; +} + +/** + * Return an #IMesh that is a triangulation of a mesh with general + * polygonal faces, #imesh. + * Added diagonals will be distinguishable by having edge original + * indices of #NO_INDEX. + */ +static IMesh triangulate_polymesh(IMesh &imesh, IMeshArena *arena) +{ + Vector<Face *> face_tris; + constexpr int estimated_tris_per_face = 3; + face_tris.reserve(estimated_tris_per_face * imesh.face_size()); + for (Face *f : imesh.faces()) { + /* Tessellate face f, following plan similar to #BM_face_calc_tesselation. */ + int flen = f->size(); + if (flen == 3) { + face_tris.append(f); + } + else if (flen == 4) { + const Vert *v0 = (*f)[0]; + const Vert *v1 = (*f)[1]; + const Vert *v2 = (*f)[2]; + const Vert *v3 = (*f)[3]; + int eo_01 = f->edge_orig[0]; + int eo_12 = f->edge_orig[1]; + int eo_23 = f->edge_orig[2]; + int eo_30 = f->edge_orig[3]; + Face *f0 = arena->add_face({v0, v1, v2}, f->orig, {eo_01, eo_12, -1}, {false, false, false}); + Face *f1 = arena->add_face({v0, v2, v3}, f->orig, {-1, eo_23, eo_30}, {false, false, false}); + face_tris.append(f0); + face_tris.append(f1); + } + else { + Array<Face *> tris = triangulate_poly(f, arena); + for (Face *tri : tris) { + face_tris.append(tri); + } + } + } + return IMesh(face_tris); +} + +/** + * If \a tri1 and \a tri2 have a common edge (in opposite orientation), + * return the indices into \a tri1 and \a tri2 where that common edge starts. Else return (-1,-1). + */ +static std::pair<int, int> find_tris_common_edge(const Face &tri1, const Face &tri2) +{ + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if (tri1[(i + 1) % 3] == tri2[j] && tri1[i] == tri2[(j + 1) % 3]) { + return std::pair<int, int>(i, j); + } + } + } + return std::pair<int, int>(-1, -1); +} + +struct MergeEdge { + /** Length (squared) of the edge, used for sorting. */ + double len_squared = 0.0; + /* v1 and v2 are the ends of the edge, ordered so that `v1->id < v2->id` */ + const Vert *v1 = nullptr; + const Vert *v2 = nullptr; + /* left_face and right_face are indices into #FaceMergeState.face. */ + int left_face = -1; + int right_face = -1; + int orig = -1; /* An edge orig index that can be used for this edge. */ + /** Is it allowed to dissolve this edge? */ + bool dissolvable = false; + /** Is this an intersect edge? */ + bool is_intersect = false; + + MergeEdge() = default; + + MergeEdge(const Vert *va, const Vert *vb) + { + if (va->id < vb->id) { + this->v1 = va; + this->v2 = vb; + } + else { + this->v1 = vb; + this->v2 = va; + } + }; +}; + +struct MergeFace { + /** The current sequence of Verts forming this face. */ + Vector<const Vert *> vert; + /** For each position in face, what is index in #FaceMergeState of edge for that position? */ + Vector<int> edge; + /** If not -1, merge_to gives a face index in #FaceMergeState that this is merged to. */ + int merge_to = -1; + /** A face->orig that can be used for the merged face. */ + int orig = -1; +}; +struct FaceMergeState { + /** + * The faces being considered for merging. Some will already have been merge (merge_to != -1). + */ + Vector<MergeFace> face; + /** + * The edges that are part of the faces in face[], together with current topological + * information (their left and right faces) and whether or not they are dissolvable. + */ + Vector<MergeEdge> edge; + /** + * `edge_map` maps a pair of `const Vert *` ids (in canonical order: smaller id first) + * to the index in the above edge vector in which to find the corresponding #MergeEdge. + */ + Map<std::pair<int, int>, int> edge_map; +}; + +static std::ostream &operator<<(std::ostream &os, const FaceMergeState &fms) +{ + os << "faces:\n"; + for (int f : fms.face.index_range()) { + const MergeFace &mf = fms.face[f]; + std::cout << f << ": orig=" << mf.orig << " verts "; + for (const Vert *v : mf.vert) { + std::cout << v << " "; + } + std::cout << "\n"; + std::cout << " edges " << mf.edge << "\n"; + std::cout << " merge_to = " << mf.merge_to << "\n"; + } + os << "\nedges:\n"; + for (int e : fms.edge.index_range()) { + const MergeEdge &me = fms.edge[e]; + std::cout << e << ": (" << me.v1 << "," << me.v2 << ") left=" << me.left_face + << " right=" << me.right_face << " dis=" << me.dissolvable << " orig=" << me.orig + << " is_int=" << me.is_intersect << "\n"; + } + return os; +} + +/** + * \a tris all have the same original face. + * Find the 2d edge/triangle topology for these triangles, but only the ones facing in the + * norm direction, and whether each edge is dissolvable or not. + */ +static void init_face_merge_state(FaceMergeState *fms, + const Vector<int> &tris, + const IMesh &tm, + const double3 &norm) +{ + constexpr int dbg_level = 0; + /* Reserve enough faces and edges so that neither will have to resize. */ + fms->face.reserve(tris.size() + 1); + fms->edge.reserve(3 * tris.size()); + fms->edge_map.reserve(3 * tris.size()); + if (dbg_level > 0) { + std::cout << "\nINIT_FACE_MERGE_STATE\n"; + } + for (int t : tris.index_range()) { + MergeFace mf; + const Face &tri = *tm.face(tris[t]); + if (dbg_level > 0) { + std::cout << "process tri = " << &tri << "\n"; + } + BLI_assert(tri.plane_populated()); + if (double3::dot(norm, tri.plane->norm) <= 0.0) { + if (dbg_level > 0) { + std::cout << "triangle has wrong orientation, skipping\n"; + } + continue; + } + mf.vert.append(tri[0]); + mf.vert.append(tri[1]); + mf.vert.append(tri[2]); + mf.orig = tri.orig; + int f = fms->face.append_and_get_index(mf); + if (dbg_level > 1) { + std::cout << "appended MergeFace for tri at f = " << f << "\n"; + } + for (int i = 0; i < 3; ++i) { + int inext = (i + 1) % 3; + MergeEdge new_me(mf.vert[i], mf.vert[inext]); + std::pair<int, int> canon_vs(new_me.v1->id, new_me.v2->id); + int me_index = fms->edge_map.lookup_default(canon_vs, -1); + if (dbg_level > 1) { + std::cout << "new_me = canon_vs = " << new_me.v1 << ", " << new_me.v2 << "\n"; + std::cout << "me_index lookup = " << me_index << "\n"; + } + if (me_index == -1) { + double3 vec = new_me.v2->co - new_me.v1->co; + new_me.len_squared = vec.length_squared(); + new_me.orig = tri.edge_orig[i]; + new_me.is_intersect = tri.is_intersect[i]; + new_me.dissolvable = (new_me.orig == NO_INDEX && !new_me.is_intersect); + fms->edge.append(new_me); + me_index = fms->edge.size() - 1; + fms->edge_map.add_new(canon_vs, me_index); + if (dbg_level > 1) { + std::cout << "added new me with me_index = " << me_index << "\n"; + std::cout << " len_squared = " << new_me.len_squared << " orig = " << new_me.orig + << ", is_intersect" << new_me.is_intersect + << ", dissolvable = " << new_me.dissolvable << "\n"; + } + } + MergeEdge &me = fms->edge[me_index]; + if (dbg_level > 1) { + std::cout << "retrieved me at index " << me_index << ":\n"; + std::cout << " v1 = " << me.v1 << " v2 = " << me.v2 << "\n"; + std::cout << " dis = " << me.dissolvable << " int = " << me.is_intersect << "\n"; + std::cout << " left_face = " << me.left_face << " right_face = " << me.right_face << "\n"; + } + if (me.dissolvable && tri.edge_orig[i] != NO_INDEX) { + if (dbg_level > 1) { + std::cout << "reassigning orig to " << tri.edge_orig[i] << ", dissolvable = false\n"; + } + me.dissolvable = false; + me.orig = tri.edge_orig[i]; + } + if (me.dissolvable && tri.is_intersect[i]) { + if (dbg_level > 1) { + std::cout << "reassigning dissolvable = false, is_intersect = true\n"; + } + me.dissolvable = false; + me.is_intersect = true; + } + /* This face is left or right depending on orientation of edge. */ + if (me.v1 == mf.vert[i]) { + if (dbg_level > 1) { + std::cout << "me.v1 == mf.vert[i] so set edge[" << me_index << "].left_face = " << f + << "\n"; + } + BLI_assert(me.left_face == -1); + fms->edge[me_index].left_face = f; + } + else { + if (dbg_level > 1) { + std::cout << "me.v1 != mf.vert[i] so set edge[" << me_index << "].right_face = " << f + << "\n"; + } + BLI_assert(me.right_face == -1); + fms->edge[me_index].right_face = f; + } + fms->face[f].edge.append(me_index); + } + } + if (dbg_level > 0) { + std::cout << *fms; + } +} + +/** + * To have a valid #BMesh, there are constraints on what edges can be removed. + * We cannot remove an edge if (a) it would create two disconnected boundary parts + * (which will happen if there's another edge sharing the same two faces); + * or (b) it would create a face with a repeated vertex. + */ +static bool dissolve_leaves_valid_bmesh(FaceMergeState *fms, + const MergeEdge &me, + int me_index, + const MergeFace &mf_left, + const MergeFace &mf_right) +{ + int a_edge_start = mf_left.edge.first_index_of_try(me_index); + BLI_assert(a_edge_start != -1); + int alen = mf_left.vert.size(); + int blen = mf_right.vert.size(); + int b_left_face = me.right_face; + bool ok = true; + /* Is there another edge, not me, in A's face, whose right face is B's left? */ + for (int a_e_index = (a_edge_start + 1) % alen; ok && a_e_index != a_edge_start; + a_e_index = (a_e_index + 1) % alen) { + const MergeEdge &a_me_cur = fms->edge[mf_left.edge[a_e_index]]; + if (a_me_cur.right_face == b_left_face) { + ok = false; + } + } + /* Is there a vert in A, not me.v1 or me.v2, that is also in B? + * One could avoid this O(n^2) algorithm if had a structure + * saying which faces a vertex touches. */ + for (int a_v_index = 0; ok && a_v_index < alen; ++a_v_index) { + const Vert *a_v = mf_left.vert[a_v_index]; + if (a_v != me.v1 && a_v != me.v2) { + for (int b_v_index = 0; b_v_index < blen; ++b_v_index) { + const Vert *b_v = mf_right.vert[b_v_index]; + if (a_v == b_v) { + ok = false; + } + } + } + } + return ok; +} + +/** + * mf_left and mf_right should share a #MergeEdge me, having index me_index. + * We change mf_left to remove edge me and insert the appropriate edges of + * mf_right in between the start and end vertices of that edge. + * We change the left face of the spliced-in edges to be mf_left's index. + * We mark the merge_to property of mf_right, which is now in essence deleted. + */ +static void splice_faces( + FaceMergeState *fms, MergeEdge &me, int me_index, MergeFace &mf_left, MergeFace &mf_right) +{ + int a_edge_start = mf_left.edge.first_index_of_try(me_index); + int b_edge_start = mf_right.edge.first_index_of_try(me_index); + BLI_assert(a_edge_start != -1 && b_edge_start != -1); + int alen = mf_left.vert.size(); + int blen = mf_right.vert.size(); + Vector<const Vert *> splice_vert; + Vector<int> splice_edge; + splice_vert.reserve(alen + blen - 2); + splice_edge.reserve(alen + blen - 2); + int ai = 0; + while (ai < a_edge_start) { + splice_vert.append(mf_left.vert[ai]); + splice_edge.append(mf_left.edge[ai]); + ++ai; + } + int bi = b_edge_start + 1; + while (bi != b_edge_start) { + if (bi >= blen) { + bi = 0; + if (bi == b_edge_start) { + break; + } + } + splice_vert.append(mf_right.vert[bi]); + splice_edge.append(mf_right.edge[bi]); + if (mf_right.vert[bi] == fms->edge[mf_right.edge[bi]].v1) { + fms->edge[mf_right.edge[bi]].left_face = me.left_face; + } + else { + fms->edge[mf_right.edge[bi]].right_face = me.left_face; + } + ++bi; + } + ai = a_edge_start + 1; + while (ai < alen) { + splice_vert.append(mf_left.vert[ai]); + splice_edge.append(mf_left.edge[ai]); + ++ai; + } + mf_right.merge_to = me.left_face; + mf_left.vert = splice_vert; + mf_left.edge = splice_edge; + me.left_face = -1; + me.right_face = -1; +} + +/** + * Given that fms has been properly initialized to contain a set of faces that + * together form a face or part of a face of the original #IMesh, and that + * it has properly recorded with faces are dissolvable, dissolve as many edges as possible. + * We try to dissolve in decreasing order of edge length, so that it is more likely + * that the final output doesn't have awkward looking long edges with extreme angles. + */ +static void do_dissolve(FaceMergeState *fms) +{ + const int dbg_level = 0; + if (dbg_level > 1) { + std::cout << "\nDO_DISSOLVE\n"; + } + Vector<int> dissolve_edges; + for (int e : fms->edge.index_range()) { + if (fms->edge[e].dissolvable) { + dissolve_edges.append(e); + } + } + if (dissolve_edges.size() == 0) { + return; + } + /* Things look nicer if we dissolve the longer edges first. */ + std::sort( + dissolve_edges.begin(), dissolve_edges.end(), [fms](const int &a, const int &b) -> bool { + return (fms->edge[a].len_squared > fms->edge[b].len_squared); + }); + if (dbg_level > 0) { + std::cout << "Sorted dissolvable edges: " << dissolve_edges << "\n"; + } + for (int me_index : dissolve_edges) { + MergeEdge &me = fms->edge[me_index]; + if (me.left_face == -1 || me.right_face == -1) { + continue; + } + MergeFace &mf_left = fms->face[me.left_face]; + MergeFace &mf_right = fms->face[me.right_face]; + if (!dissolve_leaves_valid_bmesh(fms, me, me_index, mf_left, mf_right)) { + continue; + } + if (dbg_level > 0) { + std::cout << "Removing edge " << me_index << "\n"; + } + splice_faces(fms, me, me_index, mf_left, mf_right); + if (dbg_level > 1) { + std::cout << "state after removal:\n"; + std::cout << *fms; + } + } +} + +/** + * Given that \a tris form a triangulation of a face or part of a face that was in \a imesh_in, + * merge as many of the triangles together as possible, by dissolving the edges between them. + * We can only dissolve triangulation edges that don't overlap real input edges, and we + * can only dissolve them if doing so leaves the remaining faces able to create valid #BMesh. + * We can tell edges that don't overlap real input edges because they will have an + * "original edge" that is different from #NO_INDEX. + * \note it is possible that some of the triangles in \a tris have reversed orientation + * to the rest, so we have to handle the two cases separately. + */ +static Vector<Face *> merge_tris_for_face(Vector<int> tris, + const IMesh &tm, + const IMesh &imesh_in, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "merge_tris_for_face\n"; + std::cout << "tris: " << tris << "\n"; + } + Vector<Face *> ans; + if (tris.size() <= 1) { + if (tris.size() == 1) { + ans.append(tm.face(tris[0])); + } + return ans; + } + bool done = false; + double3 first_tri_normal = tm.face(tris[0])->plane->norm; + double3 second_tri_normal = tm.face(tris[1])->plane->norm; + if (tris.size() == 2 && double3::dot(first_tri_normal, second_tri_normal) > 0.0) { + /* Is this a case where quad with one diagonal remained unchanged? + * Worth special handling because this case will be very common. */ + Face &tri1 = *tm.face(tris[0]); + Face &tri2 = *tm.face(tris[1]); + Face *in_face = imesh_in.face(tri1.orig); + if (in_face->size() == 4) { + std::pair<int, int> estarts = find_tris_common_edge(tri1, tri2); + if (estarts.first != -1 && tri1.edge_orig[estarts.first] == NO_INDEX) { + if (dbg_level > 0) { + std::cout << "try recovering orig quad case\n"; + std::cout << "tri1 = " << &tri1 << "\n"; + std::cout << "tri1 = " << &tri2 << "\n"; + } + int i0 = estarts.first; + int i1 = (i0 + 1) % 3; + int i2 = (i0 + 2) % 3; + int j2 = (estarts.second + 2) % 3; + Face tryface({tri1[i1], tri1[i2], tri1[i0], tri2[j2]}, -1, -1, {}, {}); + if (tryface.cyclic_equal(*in_face)) { + if (dbg_level > 0) { + std::cout << "inface = " << in_face << "\n"; + std::cout << "quad recovery worked\n"; + } + ans.append(in_face); + done = true; + } + } + } + } + if (done) { + return ans; + } + + double3 first_tri_normal_rev = -first_tri_normal; + for (const double3 &norm : {first_tri_normal, first_tri_normal_rev}) { + FaceMergeState fms; + init_face_merge_state(&fms, tris, tm, norm); + do_dissolve(&fms); + if (dbg_level > 0) { + std::cout << "faces in merged result:\n"; + } + for (const MergeFace &mf : fms.face) { + if (mf.merge_to == -1) { + Array<int> e_orig(mf.edge.size()); + Array<bool> is_intersect(mf.edge.size()); + for (int i : mf.edge.index_range()) { + e_orig[i] = fms.edge[mf.edge[i]].orig; + is_intersect[i] = fms.edge[mf.edge[i]].is_intersect; + } + Face *facep = arena->add_face(mf.vert, mf.orig, e_orig, is_intersect); + ans.append(facep); + if (dbg_level > 0) { + std::cout << " " << facep << "\n"; + } + } + } + } + return ans; +} + +/** + * Return an array, paralleling imesh_out.vert, saying which vertices can be dissolved. + * A vertex v can be dissolved if (a) it is not an input vertex; (b) it has valence 2; + * and (c) if v's two neighboring vertices are u and w, then (u,v,w) forms a straight line. + * Return the number of dissolvable vertices in r_count_dissolve. + */ +static Array<bool> find_dissolve_verts(IMesh &imesh_out, int *r_count_dissolve) +{ + imesh_out.populate_vert(); + /* dissolve[i] will say whether imesh_out.vert(i) can be dissolved. */ + Array<bool> dissolve(imesh_out.vert_size()); + for (int v_index : imesh_out.vert_index_range()) { + const Vert &vert = *imesh_out.vert(v_index); + dissolve[v_index] = (vert.orig == NO_INDEX); + } + /* neighbors[i] will be a pair giving the up-to-two neighboring vertices + * of the vertex v in position i of imesh_out.vert. + * If we encounter a third, then v will not be dissolvable. */ + Array<std::pair<const Vert *, const Vert *>> neighbors( + imesh_out.vert_size(), std::pair<const Vert *, const Vert *>(nullptr, nullptr)); + for (int f : imesh_out.face_index_range()) { + const Face &face = *imesh_out.face(f); + for (int i : face.index_range()) { + const Vert *v = face[i]; + int v_index = imesh_out.lookup_vert(v); + BLI_assert(v_index != NO_INDEX); + if (dissolve[v_index]) { + const Vert *n1 = face[face.next_pos(i)]; + const Vert *n2 = face[face.prev_pos(i)]; + const Vert *f_n1 = neighbors[v_index].first; + const Vert *f_n2 = neighbors[v_index].second; + if (f_n1 != nullptr) { + /* Already has a neighbor in another face; can't dissolve unless they are the same. */ + if (!((n1 == f_n2 && n2 == f_n1) || (n1 == f_n1 && n2 == f_n2))) { + /* Different neighbors, so can't dissolve. */ + dissolve[v_index] = false; + } + } + else { + /* These are the first-seen neighbors. */ + neighbors[v_index] = std::pair<const Vert *, const Vert *>(n1, n2); + } + } + } + } + int count = 0; + for (int v_out : imesh_out.vert_index_range()) { + if (dissolve[v_out]) { + dissolve[v_out] = false; /* Will set back to true if final condition is satisfied. */ + const std::pair<const Vert *, const Vert *> &nbrs = neighbors[v_out]; + if (nbrs.first != nullptr) { + BLI_assert(nbrs.second != nullptr); + const mpq3 &co1 = nbrs.first->co_exact; + const mpq3 &co2 = nbrs.second->co_exact; + const mpq3 &co = imesh_out.vert(v_out)->co_exact; + mpq3 dir1 = co - co1; + mpq3 dir2 = co2 - co; + mpq3 cross = mpq3::cross(dir1, dir2); + if (cross[0] == 0 && cross[1] == 0 && cross[2] == 0) { + dissolve[v_out] = true; + ++count; + } + } + } + } + if (r_count_dissolve != nullptr) { + *r_count_dissolve = count; + } + return dissolve; +} + +/** + * The dissolve array parallels the `imesh.vert` array. Wherever it is true, + * remove the corresponding vertex from the vertices in the faces of + * `imesh.faces` to account for the close-up of the gaps in `imesh.vert`. + */ +static void dissolve_verts(IMesh *imesh, const Array<bool> dissolve, IMeshArena *arena) +{ + constexpr int inline_face_size = 100; + Vector<bool, inline_face_size> face_pos_erase; + for (int f : imesh->face_index_range()) { + const Face &face = *imesh->face(f); + face_pos_erase.clear(); + int num_erase = 0; + for (const Vert *v : face) { + int v_index = imesh->lookup_vert(v); + BLI_assert(v_index != NO_INDEX); + if (dissolve[v_index]) { + face_pos_erase.append(true); + ++num_erase; + } + else { + face_pos_erase.append(false); + } + } + if (num_erase > 0) { + imesh->erase_face_positions(f, face_pos_erase, arena); + } + } + imesh->set_dirty_verts(); +} + +/** + * The main boolean function operates on a triangle #IMesh and produces a + * Triangle #IMesh as output. + * This function converts back into a general polygonal mesh by removing + * any possible triangulation edges (which can be identified because they + * will have an original edge that is NO_INDEX. + * Not all triangulation edges can be removed: if they ended up non-trivially overlapping a real + * input edge, then we need to keep it. Also, some are necessary to make the output satisfy + * the "valid #BMesh" property: we can't produce output faces that have repeated vertices in them, + * or have several disconnected boundaries (e.g., faces with holes). + */ +static IMesh polymesh_from_trimesh_with_dissolve(const IMesh &tm_out, + const IMesh &imesh_in, + IMeshArena *arena) +{ + const int dbg_level = 0; + if (dbg_level > 1) { + std::cout << "\nPOLYMESH_FROM_TRIMESH_WITH_DISSOLVE\n"; + } + /* For now: need plane normals for all triangles. */ + for (Face *tri : tm_out.faces()) { + tri->populate_plane(false); + } + /* Gather all output triangles that are part of each input face. + * face_output_tris[f] will be indices of triangles in tm_out + * that have f as their original face. */ + int tot_in_face = imesh_in.face_size(); + Array<Vector<int>> face_output_tris(tot_in_face); + for (int t : tm_out.face_index_range()) { + const Face &tri = *tm_out.face(t); + int in_face = tri.orig; + face_output_tris[in_face].append(t); + } + if (dbg_level > 1) { + std::cout << "face_output_tris:\n"; + for (int f : face_output_tris.index_range()) { + std::cout << f << ": " << face_output_tris[f] << "\n"; + } + } + + /* Merge triangles that we can from face_output_tri to make faces for output. + * face_output_face[f] will be new original const Face *'s that + * make up whatever part of the boolean output remains of input face f. */ + Array<Vector<Face *>> face_output_face(tot_in_face); + int tot_out_face = 0; + for (int in_f : imesh_in.face_index_range()) { + if (dbg_level > 1) { + std::cout << "merge tris for face " << in_f << "\n"; + } + int num_out_tris_for_face = face_output_tris.size(); + if (num_out_tris_for_face == 0) { + continue; + } + face_output_face[in_f] = merge_tris_for_face(face_output_tris[in_f], tm_out, imesh_in, arena); + tot_out_face += face_output_face[in_f].size(); + } + Array<Face *> face(tot_out_face); + int out_f_index = 0; + for (int in_f : imesh_in.face_index_range()) { + const Vector<Face *> &f_faces = face_output_face[in_f]; + if (f_faces.size() > 0) { + std::copy(f_faces.begin(), f_faces.end(), &face[out_f_index]); + out_f_index += f_faces.size(); + } + } + IMesh imesh_out(face); + /* Dissolve vertices that were (a) not original; and (b) now have valence 2 and + * are between two other vertices that are exactly in line with them. + * These were created because of triangulation edges that have been dissolved. */ + int count_dissolve; + Array<bool> v_dissolve = find_dissolve_verts(imesh_out, &count_dissolve); + if (count_dissolve > 0) { + dissolve_verts(&imesh_out, v_dissolve, arena); + } + if (dbg_level > 1) { + write_obj_mesh(imesh_out, "boolean_post_dissolve"); + } + + return imesh_out; +} + +/** + * This function does a boolean operation on a TriMesh with nshapes inputs. + * All the shapes are combined in tm_in. + * The shape_fn function should take a triangle index in tm_in and return + * a number in the range 0 to `nshapes-1`, to say which shape that triangle is in. + */ +IMesh boolean_trimesh(IMesh &tm_in, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "BOOLEAN of " << nshapes << " operand" << (nshapes == 1 ? "" : "s") + << " op=" << bool_optype_name(op) << "\n"; + if (dbg_level > 1) { + tm_in.populate_vert(); + std::cout << "boolean_trimesh input:\n" << tm_in; + write_obj_mesh(tm_in, "boolean_in"); + } + } + if (tm_in.face_size() == 0) { + return IMesh(tm_in); + } + IMesh tm_si; + if (use_self) { + tm_si = trimesh_self_intersect(tm_in, arena); + } + else { + tm_si = trimesh_nary_intersect(tm_in, nshapes, shape_fn, use_self, arena); + } + if (dbg_level > 1) { + write_obj_mesh(tm_si, "boolean_tm_si"); + std::cout << "\nboolean_tm_input after intersection:\n" << tm_si; + } + /* It is possible for tm_si to be empty if all the input triangles are bogus/degenerate. */ + if (tm_si.face_size() == 0 || op == BoolOpType::None) { + return tm_si; + } + auto si_shape_fn = [shape_fn, tm_si](int t) { return shape_fn(tm_si.face(t)->orig); }; + TriMeshTopology tm_si_topo(tm_si); + PatchesInfo pinfo = find_patches(tm_si, tm_si_topo); + IMesh tm_out; + if (!is_pwn(tm_si, tm_si_topo)) { + if (dbg_level > 0) { + std::cout << "Input is not PWN, using gwn method\n"; + } + tm_out = gwn_boolean(tm_si, op, nshapes, shape_fn, pinfo, arena); + } + else { + CellsInfo cinfo = find_cells(tm_si, tm_si_topo, pinfo); + if (dbg_level > 0) { + std::cout << "Input is PWN\n"; + } + finish_patch_cell_graph(tm_si, cinfo, pinfo, tm_si_topo, arena); + bool pc_ok = patch_cell_graph_ok(cinfo, pinfo); + if (!pc_ok) { + /* TODO: if bad input can lead to this, diagnose the problem. */ + std::cout << "Something funny about input or a bug in boolean\n"; + return IMesh(tm_in); + } + cinfo.init_windings(nshapes); + int c_ambient = find_ambient_cell(tm_si, nullptr, tm_si_topo, pinfo, arena); + if (c_ambient == NO_INDEX) { + /* TODO: find a way to propagate this error to user properly. */ + std::cout << "Could not find an ambient cell; input not valid?\n"; + return IMesh(tm_si); + } + propagate_windings_and_in_output_volume(pinfo, cinfo, c_ambient, op, nshapes, si_shape_fn); + tm_out = extract_from_in_output_volume_diffs(tm_si, pinfo, cinfo, arena); + if (dbg_level > 0) { + /* Check if output is PWN. */ + TriMeshTopology tm_out_topo(tm_out); + if (!is_pwn(tm_out, tm_out_topo)) { + std::cout << "OUTPUT IS NOT PWN!\n"; + } + } + } + if (dbg_level > 1) { + write_obj_mesh(tm_out, "boolean_tm_output"); + std::cout << "boolean tm output:\n" << tm_out; + } + return tm_out; +} + +static void dump_test_spec(IMesh &imesh) +{ + std::cout << "test spec = " << imesh.vert_size() << " " << imesh.face_size() << "\n"; + for (const Vert *v : imesh.vertices()) { + std::cout << v->co_exact[0] << " " << v->co_exact[1] << " " << v->co_exact[2] << " # " + << v->co[0] << " " << v->co[1] << " " << v->co[2] << "\n"; + } + for (const Face *f : imesh.faces()) { + for (const Vert *fv : *f) { + std::cout << imesh.lookup_vert(fv) << " "; + } + std::cout << "\n"; + } +} + +/** + * Do the boolean operation op on the polygon mesh imesh_in. + * See the header file for a complete description. + */ +IMesh boolean_mesh(IMesh &imesh, + BoolOpType op, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMesh *imesh_triangulated, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nBOOLEAN_MESH\n" + << nshapes << " operand" << (nshapes == 1 ? "" : "s") + << " op=" << bool_optype_name(op) << "\n"; + if (dbg_level > 1) { + write_obj_mesh(imesh, "boolean_mesh_in"); + std::cout << imesh; + if (dbg_level > 2) { + dump_test_spec(imesh); + } + } + } + IMesh *tm_in = imesh_triangulated; + IMesh our_triangulation; + if (tm_in == nullptr) { + our_triangulation = triangulate_polymesh(imesh, arena); + tm_in = &our_triangulation; + } + if (dbg_level > 1) { + write_obj_mesh(*tm_in, "boolean_tm_in"); + } + IMesh tm_out = boolean_trimesh(*tm_in, op, nshapes, shape_fn, use_self, arena); + if (dbg_level > 1) { + std::cout << "bool_trimesh_output:\n" << tm_out; + write_obj_mesh(tm_out, "bool_trimesh_output"); + } + IMesh ans = polymesh_from_trimesh_with_dissolve(tm_out, imesh, arena); + if (dbg_level > 0) { + std::cout << "boolean_mesh output:\n" << ans; + } + return ans; +} + +} // namespace blender::meshintersect + +#endif // WITH_GMP diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc new file mode 100644 index 00000000000..d973775a797 --- /dev/null +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -0,0 +1,3304 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bli + */ + +/* The #blender::meshintersect API needs GMP. */ +#ifdef WITH_GMP + +# include <algorithm> +# include <fstream> +# include <iostream> + +# include "BLI_allocator.hh" +# include "BLI_array.hh" +# include "BLI_assert.h" +# include "BLI_delaunay_2d.h" +# include "BLI_double3.hh" +# include "BLI_float3.hh" +# include "BLI_hash.hh" +# include "BLI_kdopbvh.h" +# include "BLI_map.hh" +# include "BLI_math_boolean.hh" +# include "BLI_math_mpq.hh" +# include "BLI_mpq2.hh" +# include "BLI_mpq3.hh" +# include "BLI_span.hh" +# include "BLI_task.h" +# include "BLI_threads.h" +# include "BLI_vector.hh" +# include "BLI_vector_set.hh" + +# include "PIL_time.h" + +# include "BLI_mesh_intersect.hh" + +// # define PERFDEBUG + +namespace blender::meshintersect { + +# ifdef PERFDEBUG +static void perfdata_init(void); +static void incperfcount(int countnum); +static void bumpperfcount(int countnum, int amt); +static void doperfmax(int maxnum, int val); +static void dump_perfdata(void); +# endif + +/** For debugging, can disable threading in intersect code with this static constant. */ +static constexpr bool intersect_use_threading = true; + +Vert::Vert(const mpq3 &mco, const double3 &dco, int id, int orig) + : co_exact(mco), co(dco), id(id), orig(orig) +{ +} + +bool Vert::operator==(const Vert &other) const +{ + return this->co_exact == other.co_exact; +} + +uint64_t Vert::hash() const +{ + return co_exact.hash(); +} + +std::ostream &operator<<(std::ostream &os, const Vert *v) +{ + os << "v" << v->id; + if (v->orig != NO_INDEX) { + os << "o" << v->orig; + } + os << v->co; + return os; +} + +bool Plane::operator==(const Plane &other) const +{ + return norm_exact == other.norm_exact && d_exact == other.d_exact; +} + +void Plane::make_canonical() +{ + if (norm_exact[0] != 0) { + mpq_class den = norm_exact[0]; + norm_exact = mpq3(1, norm_exact[1] / den, norm_exact[2] / den); + d_exact = d_exact / den; + } + else if (norm_exact[1] != 0) { + mpq_class den = norm_exact[1]; + norm_exact = mpq3(0, 1, norm_exact[2] / den); + d_exact = d_exact / den; + } + else { + if (norm_exact[2] != 0) { + mpq_class den = norm_exact[2]; + norm_exact = mpq3(0, 0, 1); + d_exact = d_exact / den; + } + else { + /* A degenerate plane. */ + d_exact = 0; + } + } + norm = double3(norm_exact[0].get_d(), norm_exact[1].get_d(), norm_exact[2].get_d()); + d = d_exact.get_d(); +} + +Plane::Plane(const mpq3 &norm_exact, const mpq_class &d_exact) + : norm_exact(norm_exact), d_exact(d_exact) +{ + norm = double3(norm_exact[0].get_d(), norm_exact[1].get_d(), norm_exact[2].get_d()); + d = d_exact.get_d(); +} + +Plane::Plane(const double3 &norm, const double d) : norm(norm), d(d) +{ + norm_exact = mpq3(0, 0, 0); /* Marks as "exact not yet populated". */ +} + +/** This is wrong for degenerate planes, but we don't expect to call it on those. */ +bool Plane::exact_populated() const +{ + return norm_exact[0] != 0 || norm_exact[1] != 0 || norm_exact[2] != 0; +} + +uint64_t Plane::hash() const +{ + constexpr uint64_t h1 = 33; + constexpr uint64_t h2 = 37; + constexpr uint64_t h3 = 39; + uint64_t hashx = hash_mpq_class(this->norm_exact.x); + uint64_t hashy = hash_mpq_class(this->norm_exact.y); + uint64_t hashz = hash_mpq_class(this->norm_exact.z); + uint64_t hashd = hash_mpq_class(this->d_exact); + uint64_t ans = hashx ^ (hashy * h1) ^ (hashz * h1 * h2) ^ (hashd * h1 * h2 * h3); + return ans; +} + +std::ostream &operator<<(std::ostream &os, const Plane *plane) +{ + os << "[" << plane->norm << ";" << plane->d << "]"; + return os; +} + +Face::Face( + Span<const Vert *> verts, int id, int orig, Span<int> edge_origs, Span<bool> is_intersect) + : vert(verts), edge_orig(edge_origs), is_intersect(is_intersect), id(id), orig(orig) +{ +} + +Face::Face(Span<const Vert *> verts, int id, int orig) : vert(verts), id(id), orig(orig) +{ +} + +void Face::populate_plane(bool need_exact) +{ + if (plane != nullptr) { + if (!need_exact || plane->exact_populated()) { + return; + } + } + if (need_exact) { + mpq3 normal_exact; + if (vert.size() > 3) { + Array<mpq3> co(vert.size()); + for (int i : index_range()) { + co[i] = vert[i]->co_exact; + } + normal_exact = mpq3::cross_poly(co); + } + else { + mpq3 tr02 = vert[0]->co_exact - vert[2]->co_exact; + mpq3 tr12 = vert[1]->co_exact - vert[2]->co_exact; + normal_exact = mpq3::cross(tr02, tr12); + } + mpq_class d_exact = -mpq3::dot(normal_exact, vert[0]->co_exact); + plane = new Plane(normal_exact, d_exact); + } + else { + double3 normal; + if (vert.size() > 3) { + Array<double3> co(vert.size()); + for (int i : index_range()) { + co[i] = vert[i]->co; + } + normal = double3::cross_poly(co); + } + else { + double3 tr02 = vert[0]->co - vert[2]->co; + double3 tr12 = vert[1]->co - vert[2]->co; + normal = double3::cross_high_precision(tr02, tr12); + } + double d = -double3::dot(normal, vert[0]->co); + plane = new Plane(normal, d); + } +} + +Face::~Face() +{ + if (plane != nullptr) { + delete plane; + } +} + +bool Face::operator==(const Face &other) const +{ + if (this->size() != other.size()) { + return false; + } + for (FacePos i : index_range()) { + /* Can test pointer equality since we will have + * unique vert pointers for unique co_equal's. */ + if (this->vert[i] != other.vert[i]) { + return false; + } + } + return true; +} + +bool Face::cyclic_equal(const Face &other) const +{ + if (this->size() != other.size()) { + return false; + } + int flen = this->size(); + for (FacePos start : index_range()) { + for (FacePos start_other : index_range()) { + bool ok = true; + for (int i = 0; ok && i < flen; ++i) { + FacePos p = (start + i) % flen; + FacePos p_other = (start_other + i) % flen; + if (this->vert[p] != other.vert[p_other]) { + ok = false; + } + } + if (ok) { + return true; + } + } + } + return false; +} + +std::ostream &operator<<(std::ostream &os, const Face *f) +{ + os << "f" << f->id << "o" << f->orig << "["; + for (const Vert *v : *f) { + os << "v" << v->id; + if (v->orig != NO_INDEX) { + os << "o" << v->orig; + } + if (v != f->vert[f->size() - 1]) { + os << " "; + } + } + os << "]"; + if (f->orig != NO_INDEX) { + os << "o" << f->orig; + } + os << " e_orig["; + for (int i : f->index_range()) { + os << f->edge_orig[i]; + if (f->is_intersect[i]) { + os << "#"; + } + if (i != f->size() - 1) { + os << " "; + } + } + os << "]"; + return os; +} + +/** + * Un-comment the following to try using a spin-lock instead of + * a mutex in the arena allocation routines. + * Initial tests showed that it doesn't seem to help very much, + * if at all, to use a spin-lock. + */ +// #define USE_SPINLOCK + +/** + * #IMeshArena is the owner of the Vert and Face resources used + * during a run of one of the mesh-intersect main functions. + * It also keeps has a hash table of all Verts created so that it can + * ensure that only one instance of a Vert with a given co_exact will + * exist. I.e., it de-duplicates the vertices. + */ +class IMeshArena::IMeshArenaImpl : NonCopyable, NonMovable { + + /** + * Don't use Vert itself as key since resizing may move + * pointers to the Vert around, and we need to have those pointers + * stay the same throughout the lifetime of the #IMeshArena. + */ + struct VSetKey { + Vert *vert; + + VSetKey(Vert *p) : vert(p) + { + } + + uint32_t hash() const + { + return vert->hash(); + } + + bool operator==(const VSetKey &other) const + { + return *this->vert == *other.vert; + } + }; + + VectorSet<VSetKey> vset_; /* TODO: replace with Set */ + + /** + * Ownership of the Vert memory is here, so destroying this reclaims that memory. + * + * TODO: replace these with pooled allocation, and just destroy the pools at the end. + */ + Vector<std::unique_ptr<Vert>> allocated_verts_; + Vector<std::unique_ptr<Face>> allocated_faces_; + + /* Use these to allocate ids when Verts and Faces are allocated. */ + int next_vert_id_ = 0; + int next_face_id_ = 0; + + /* Need a lock when multi-threading to protect allocation of new elements. */ +# ifdef USE_SPINLOCK + SpinLock lock_; +# else + ThreadMutex *mutex_; +# endif + + public: + IMeshArenaImpl() + { + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_init(&lock_); +# else + mutex_ = BLI_mutex_alloc(); +# endif + } + } + ~IMeshArenaImpl() + { + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_end(&lock_); +# else + BLI_mutex_free(mutex_); +# endif + } + } + + void reserve(int vert_num_hint, int face_num_hint) + { + vset_.reserve(vert_num_hint); + allocated_verts_.reserve(vert_num_hint); + allocated_faces_.reserve(face_num_hint); + } + + int tot_allocated_verts() const + { + return allocated_verts_.size(); + } + + int tot_allocated_faces() const + { + return allocated_faces_.size(); + } + + const Vert *add_or_find_vert(const mpq3 &co, int orig) + { + double3 dco(co[0].get_d(), co[1].get_d(), co[2].get_d()); + return add_or_find_vert(co, dco, orig); + } + + const Vert *add_or_find_vert(const double3 &co, int orig) + { + mpq3 mco(co[0], co[1], co[2]); + return add_or_find_vert(mco, co, orig); + } + + Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs, Span<bool> is_intersect) + { + Face *f = new Face(verts, next_face_id_++, orig, edge_origs, is_intersect); + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_lock(&lock_); +# else + BLI_mutex_lock(mutex_); +# endif + } + allocated_faces_.append(std::unique_ptr<Face>(f)); + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_unlock(&lock_); +# else + BLI_mutex_unlock(mutex_); +# endif + } + return f; + } + + Face *add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs) + { + Array<bool> is_intersect(verts.size(), false); + return add_face(verts, orig, edge_origs, is_intersect); + } + + Face *add_face(Span<const Vert *> verts, int orig) + { + Array<int> edge_origs(verts.size(), NO_INDEX); + Array<bool> is_intersect(verts.size(), false); + return add_face(verts, orig, edge_origs, is_intersect); + } + + const Vert *find_vert(const mpq3 &co) + { + const Vert *ans; + Vert vtry(co, double3(), NO_INDEX, NO_INDEX); + VSetKey vskey(&vtry); + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_lock(&lock_); +# else + BLI_mutex_lock(mutex_); +# endif + } + int i = vset_.index_of_try(vskey); + if (i == -1) { + ans = nullptr; + } + else { + ans = vset_[i].vert; + } + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_unlock(&lock_); +# else + BLI_mutex_unlock(mutex_); +# endif + } + return ans; + } + + /** + * This is slow. Only used for unit tests right now. + * Since it is only used for that purpose, access is not lock-protected. + * The argument vs can be a cyclic shift of the actual stored Face. + */ + const Face *find_face(Span<const Vert *> vs) + { + Array<int> eorig(vs.size(), NO_INDEX); + Array<bool> is_intersect(vs.size(), false); + Face ftry(vs, NO_INDEX, NO_INDEX, eorig, is_intersect); + for (const int i : allocated_faces_.index_range()) { + if (ftry.cyclic_equal(*allocated_faces_[i])) { + return allocated_faces_[i].get(); + } + } + return nullptr; + } + + private: + const Vert *add_or_find_vert(const mpq3 &mco, const double3 &dco, int orig) + { + /* Don't allocate Vert yet, in case it is already there. */ + Vert vtry(mco, dco, NO_INDEX, NO_INDEX); + const Vert *ans; + VSetKey vskey(&vtry); + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_lock(&lock_); +# else + BLI_mutex_lock(mutex_); +# endif + } + int i = vset_.index_of_try(vskey); + if (i == -1) { + vskey.vert = new Vert(mco, dco, next_vert_id_++, orig); + vset_.add_new(vskey); + allocated_verts_.append(std::unique_ptr<Vert>(vskey.vert)); + ans = vskey.vert; + } + else { + /* It was a duplicate, so return the existing one. + * Note that the returned Vert may have a different orig. + * This is the intended semantics: if the Vert already + * exists then we are merging verts and using the first-seen + * one as the canonical one. */ + ans = vset_[i].vert; + } + if (intersect_use_threading) { +# ifdef USE_SPINLOCK + BLI_spin_unlock(&lock_); +# else + BLI_mutex_unlock(mutex_); +# endif + } + return ans; + }; +}; + +IMeshArena::IMeshArena() +{ + pimpl_ = std::unique_ptr<IMeshArenaImpl>(new IMeshArenaImpl()); +} + +IMeshArena::~IMeshArena() +{ +} + +void IMeshArena::reserve(int vert_num_hint, int face_num_hint) +{ + pimpl_->reserve(vert_num_hint, face_num_hint); +} + +int IMeshArena::tot_allocated_verts() const +{ + return pimpl_->tot_allocated_verts(); +} + +int IMeshArena::tot_allocated_faces() const +{ + return pimpl_->tot_allocated_faces(); +} + +const Vert *IMeshArena::add_or_find_vert(const mpq3 &co, int orig) +{ + return pimpl_->add_or_find_vert(co, orig); +} + +Face *IMeshArena::add_face(Span<const Vert *> verts, + int orig, + Span<int> edge_origs, + Span<bool> is_intersect) +{ + return pimpl_->add_face(verts, orig, edge_origs, is_intersect); +} + +Face *IMeshArena::add_face(Span<const Vert *> verts, int orig, Span<int> edge_origs) +{ + return pimpl_->add_face(verts, orig, edge_origs); +} + +Face *IMeshArena::add_face(Span<const Vert *> verts, int orig) +{ + return pimpl_->add_face(verts, orig); +} + +const Vert *IMeshArena::add_or_find_vert(const double3 &co, int orig) +{ + return pimpl_->add_or_find_vert(co, orig); +} + +const Vert *IMeshArena::find_vert(const mpq3 &co) const +{ + return pimpl_->find_vert(co); +} + +const Face *IMeshArena::find_face(Span<const Vert *> verts) const +{ + return pimpl_->find_face(verts); +} + +void IMesh::set_faces(Span<Face *> faces) +{ + face_ = faces; +} + +int IMesh::lookup_vert(const Vert *v) const +{ + BLI_assert(vert_populated_); + return vert_to_index_.lookup_default(v, NO_INDEX); +} + +void IMesh::populate_vert() +{ + /* This is likely an overestimate, since verts are shared between + * faces. It is ok if estimate is over or even under. */ + constexpr int ESTIMATE_VERTS_PER_FACE = 4; + int estimate_num_verts = ESTIMATE_VERTS_PER_FACE * face_.size(); + populate_vert(estimate_num_verts); +} + +void IMesh::populate_vert(int max_verts) +{ + if (vert_populated_) { + return; + } + vert_to_index_.reserve(max_verts); + int next_allocate_index = 0; + for (const Face *f : face_) { + for (const Vert *v : *f) { + if (v->id == 1) { + } + int index = vert_to_index_.lookup_default(v, NO_INDEX); + if (index == NO_INDEX) { + BLI_assert(next_allocate_index < UINT_MAX - 2); + vert_to_index_.add(v, next_allocate_index++); + } + } + } + int tot_v = next_allocate_index; + vert_ = Array<const Vert *>(tot_v); + for (auto item : vert_to_index_.items()) { + int index = item.value; + BLI_assert(index < tot_v); + vert_[index] = item.key; + } + /* Easier debugging (at least when there are no merged input verts) + * if output vert order is same as input, with new verts at the end. + * TODO: when all debugged, set fix_order = false. */ + const bool fix_order = true; + if (fix_order) { + std::sort(vert_.begin(), vert_.end(), [](const Vert *a, const Vert *b) { + if (a->orig != NO_INDEX && b->orig != NO_INDEX) { + return a->orig < b->orig; + } + if (a->orig != NO_INDEX) { + return true; + } + if (b->orig != NO_INDEX) { + return false; + } + return a->id < b->id; + }); + for (int i : vert_.index_range()) { + const Vert *v = vert_[i]; + vert_to_index_.add_overwrite(v, i); + } + } + vert_populated_ = true; +} + +void IMesh::erase_face_positions(int f_index, Span<bool> face_pos_erase, IMeshArena *arena) +{ + const Face *cur_f = this->face(f_index); + int cur_len = cur_f->size(); + int num_to_erase = 0; + for (int i : cur_f->index_range()) { + if (face_pos_erase[i]) { + ++num_to_erase; + } + } + if (num_to_erase == 0) { + return; + } + int new_len = cur_len - num_to_erase; + if (new_len < 3) { + /* Invalid erase. Don't do anything. */ + return; + } + Array<const Vert *> new_vert(new_len); + Array<int> new_edge_orig(new_len); + Array<bool> new_is_intersect(new_len); + int new_index = 0; + for (int i : cur_f->index_range()) { + if (!face_pos_erase[i]) { + new_vert[new_index] = (*cur_f)[i]; + new_edge_orig[new_index] = cur_f->edge_orig[i]; + new_is_intersect[new_index] = cur_f->is_intersect[i]; + ++new_index; + } + } + BLI_assert(new_index == new_len); + this->face_[f_index] = arena->add_face(new_vert, cur_f->orig, new_edge_orig, new_is_intersect); +} + +std::ostream &operator<<(std::ostream &os, const IMesh &mesh) +{ + if (mesh.has_verts()) { + os << "Verts:\n"; + int i = 0; + for (const Vert *v : mesh.vertices()) { + os << i << ": " << v << "\n"; + ++i; + } + } + os << "\nFaces:\n"; + int i = 0; + for (const Face *f : mesh.faces()) { + os << i << ": " << f << "\n"; + if (f->plane != nullptr) { + os << " plane=" << f->plane << " eorig=["; + for (Face::FacePos p = 0; p < f->size(); ++p) { + os << f->edge_orig[p] << " "; + } + os << "]\n"; + } + ++i; + } + return os; +} + +struct BoundingBox { + float3 min{FLT_MAX, FLT_MAX, FLT_MAX}; + float3 max{-FLT_MAX, -FLT_MAX, -FLT_MAX}; + + BoundingBox() = default; + BoundingBox(const float3 &min, const float3 &max) : min(min), max(max) + { + } + BoundingBox(const BoundingBox &other) : min(other.min), max(other.max) + { + } + BoundingBox(BoundingBox &&other) noexcept : min(std::move(other.min)), max(std::move(other.max)) + { + } + ~BoundingBox() = default; + BoundingBox operator=(const BoundingBox &other) + { + if (this != &other) { + min = other.min; + max = other.max; + } + return *this; + } + BoundingBox operator=(BoundingBox &&other) noexcept + { + min = std::move(other.min); + max = std::move(other.max); + return *this; + } + + void combine(const float3 &p) + { + min.x = min_ff(min.x, p.x); + min.y = min_ff(min.y, p.y); + min.z = min_ff(min.z, p.z); + max.x = max_ff(max.x, p.x); + max.y = max_ff(max.y, p.y); + max.z = max_ff(max.z, p.z); + } + + void combine(const double3 &p) + { + min.x = min_ff(min.x, static_cast<float>(p.x)); + min.y = min_ff(min.y, static_cast<float>(p.y)); + min.z = min_ff(min.z, static_cast<float>(p.z)); + max.x = max_ff(max.x, static_cast<float>(p.x)); + max.y = max_ff(max.y, static_cast<float>(p.y)); + max.z = max_ff(max.z, static_cast<float>(p.z)); + } + + void combine(const BoundingBox &bb) + { + min.x = min_ff(min.x, bb.min.x); + min.y = min_ff(min.y, bb.min.y); + min.z = min_ff(min.z, bb.min.z); + max.x = max_ff(max.x, bb.max.x); + max.y = max_ff(max.y, bb.max.y); + max.z = max_ff(max.z, bb.max.z); + } + + void expand(float pad) + { + min.x -= pad; + min.y -= pad; + min.z -= pad; + max.x += pad; + max.y += pad; + max.z += pad; + } +}; + +/** + * Assume bounding boxes have been expanded by a sufficient epsilon on all sides + * so that the comparisons against the bb bounds are sufficient to guarantee that + * if an overlap or even touching could happen, this will return true. + */ +static bool bbs_might_intersect(const BoundingBox &bb_a, const BoundingBox &bb_b) +{ + return isect_aabb_aabb_v3(bb_a.min, bb_a.max, bb_b.min, bb_b.max); +} + +/** + * Data and functions to calculate bounding boxes and pad them, in parallel. + * The bounding box calculation has the additional task of calculating the maximum + * absolute value of any coordinate in the mesh, which will be used to calculate + * the pad value. + */ +struct BBChunkData { + double max_abs_val = 0.0; +}; + +struct BBCalcData { + const IMesh &im; + Array<BoundingBox> *face_bounding_box; + + BBCalcData(const IMesh &im, Array<BoundingBox> *fbb) : im(im), face_bounding_box(fbb){}; +}; + +static void calc_face_bb_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) +{ + BBCalcData *bbdata = static_cast<BBCalcData *>(userdata); + double max_abs = 0.0; + const Face &face = *bbdata->im.face(iter); + BoundingBox &bb = (*bbdata->face_bounding_box)[iter]; + for (const Vert *v : face) { + bb.combine(v->co); + for (int i = 0; i < 3; ++i) { + max_abs = max_dd(max_abs, fabs(v->co[i])); + } + } + BBChunkData *chunk = static_cast<BBChunkData *>(tls->userdata_chunk); + chunk->max_abs_val = max_dd(max_abs, chunk->max_abs_val); +} + +struct BBPadData { + Array<BoundingBox> *face_bounding_box; + double pad; + + BBPadData(Array<BoundingBox> *fbb, double pad) : face_bounding_box(fbb), pad(pad){}; +}; + +static void pad_face_bb_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + BBPadData *pad_data = static_cast<BBPadData *>(userdata); + (*pad_data->face_bounding_box)[iter].expand(pad_data->pad); +} + +static void calc_face_bb_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + BBChunkData *bbchunk_join = static_cast<BBChunkData *>(chunk_join); + BBChunkData *bbchunk = static_cast<BBChunkData *>(chunk); + bbchunk_join->max_abs_val = max_dd(bbchunk_join->max_abs_val, bbchunk->max_abs_val); +} + +/** + * We will expand the bounding boxes by an epsilon on all sides so that + * the "less than" tests in isect_aabb_aabb_v3 are sufficient to detect + * touching or overlap. + */ +static Array<BoundingBox> calc_face_bounding_boxes(const IMesh &m) +{ + int n = m.face_size(); + Array<BoundingBox> ans(n); + TaskParallelSettings settings; + BBCalcData data(m, &ans); + BBChunkData chunk_data; + BLI_parallel_range_settings_defaults(&settings); + settings.userdata_chunk = &chunk_data; + settings.userdata_chunk_size = sizeof(chunk_data); + settings.func_reduce = calc_face_bb_reduce; + settings.min_iter_per_thread = 1000; + settings.use_threading = intersect_use_threading; + BLI_task_parallel_range(0, n, &data, calc_face_bb_range_func, &settings); + double max_abs_val = chunk_data.max_abs_val; + constexpr float pad_factor = 10.0f; + float pad = max_abs_val == 0.0f ? FLT_EPSILON : 2 * FLT_EPSILON * max_abs_val; + pad *= pad_factor; /* For extra safety. */ + TaskParallelSettings pad_settings; + BLI_parallel_range_settings_defaults(&pad_settings); + settings.min_iter_per_thread = 1000; + settings.use_threading = intersect_use_threading; + BBPadData pad_data(&ans, pad); + BLI_task_parallel_range(0, n, &pad_data, pad_face_bb_range_func, &pad_settings); + return ans; +} + +/** + * A cluster of co-planar triangles, by index. + * A pair of triangles T0 and T1 is said to "non-trivially co-planar-intersect" + * if they are co-planar, intersect, and their intersection is not just existing + * elements (verts, edges) of both triangles. + * A co-planar cluster is said to be "nontrivial" if it has more than one triangle + * and every triangle in it non-trivially co-planar-intersects with at least one other + * triangle in the cluster. + */ +class CoplanarCluster { + Vector<int> tris_; + BoundingBox bb_; + + public: + CoplanarCluster() = default; + CoplanarCluster(int t, const BoundingBox &bb) + { + this->add_tri(t, bb); + } + CoplanarCluster(const CoplanarCluster &other) : tris_(other.tris_), bb_(other.bb_) + { + } + CoplanarCluster(CoplanarCluster &&other) noexcept + : tris_(std::move(other.tris_)), bb_(std::move(other.bb_)) + { + } + ~CoplanarCluster() = default; + CoplanarCluster &operator=(const CoplanarCluster &other) + { + if (this != &other) { + tris_ = other.tris_; + bb_ = other.bb_; + } + return *this; + } + CoplanarCluster &operator=(CoplanarCluster &&other) noexcept + { + tris_ = std::move(other.tris_); + bb_ = std::move(other.bb_); + return *this; + } + + /* Assume that caller knows this will not be a duplicate. */ + void add_tri(int t, const BoundingBox &bb) + { + tris_.append(t); + bb_ = bb; + } + int tot_tri() const + { + return tris_.size(); + } + int tri(int index) const + { + return tris_[index]; + } + const int *begin() const + { + return tris_.begin(); + } + const int *end() const + { + return tris_.end(); + } + + const BoundingBox &bounding_box() const + { + return bb_; + } +}; + +/** + * Maintains indexed set of #CoplanarCluster, with the added ability + * to efficiently find the cluster index of any given triangle + * (the max triangle index needs to be given in the initializer). + * The #tri_cluster(t) function returns -1 if t is not part of any cluster. + */ +class CoplanarClusterInfo { + Vector<CoplanarCluster> clusters_; + Array<int> tri_cluster_; + + public: + CoplanarClusterInfo() = default; + explicit CoplanarClusterInfo(int numtri) : tri_cluster_(Array<int>(numtri)) + { + tri_cluster_.fill(-1); + } + + int tri_cluster(int t) const + { + BLI_assert(t < tri_cluster_.size()); + return tri_cluster_[t]; + } + + int add_cluster(CoplanarCluster cl) + { + int c_index = clusters_.append_and_get_index(cl); + for (int t : cl) { + BLI_assert(t < tri_cluster_.size()); + tri_cluster_[t] = c_index; + } + return c_index; + } + + int tot_cluster() const + { + return clusters_.size(); + } + + const CoplanarCluster *begin() + { + return clusters_.begin(); + } + + const CoplanarCluster *end() + { + return clusters_.end(); + } + + IndexRange index_range() const + { + return clusters_.index_range(); + } + + const CoplanarCluster &cluster(int index) const + { + BLI_assert(index < clusters_.size()); + return clusters_[index]; + } +}; + +static std::ostream &operator<<(std::ostream &os, const CoplanarCluster &cl); + +static std::ostream &operator<<(std::ostream &os, const CoplanarClusterInfo &clinfo); + +enum ITT_value_kind { INONE, IPOINT, ISEGMENT, ICOPLANAR }; + +struct ITT_value { + enum ITT_value_kind kind; + mpq3 p1; /* Only relevant for IPOINT and ISEGMENT kind. */ + mpq3 p2; /* Only relevant for ISEGMENT kind. */ + int t_source; /* Index of the source triangle that intersected the target one. */ + + ITT_value() : kind(INONE), t_source(-1) + { + } + ITT_value(ITT_value_kind k) : kind(k), t_source(-1) + { + } + ITT_value(ITT_value_kind k, int tsrc) : kind(k), t_source(tsrc) + { + } + ITT_value(ITT_value_kind k, const mpq3 &p1) : kind(k), p1(p1), t_source(-1) + { + } + ITT_value(ITT_value_kind k, const mpq3 &p1, const mpq3 &p2) + : kind(k), p1(p1), p2(p2), t_source(-1) + { + } + ITT_value(const ITT_value &other) + : kind(other.kind), p1(other.p1), p2(other.p2), t_source(other.t_source) + { + } + ITT_value(ITT_value &&other) noexcept + : kind(other.kind), + p1(std::move(other.p1)), + p2(std::move(other.p2)), + t_source(other.t_source) + { + } + ~ITT_value() + { + } + ITT_value &operator=(const ITT_value &other) + { + if (this != &other) { + kind = other.kind; + p1 = other.p1; + p2 = other.p2; + t_source = other.t_source; + } + return *this; + } + ITT_value &operator=(ITT_value &&other) noexcept + { + kind = other.kind; + p1 = std::move(other.p1); + p2 = std::move(other.p2); + t_source = other.t_source; + return *this; + } +}; + +static std::ostream &operator<<(std::ostream &os, const ITT_value &itt); + +/** + * Project a 3d vert to a 2d one by eliding proj_axis. This does not create + * degeneracies as long as the projection axis is one where the corresponding + * component of the originating plane normal is non-zero. + */ +static mpq2 project_3d_to_2d(const mpq3 &p3d, int proj_axis) +{ + mpq2 p2d; + switch (proj_axis) { + case (0): { + p2d[0] = p3d[1]; + p2d[1] = p3d[2]; + break; + } + case (1): { + p2d[0] = p3d[0]; + p2d[1] = p3d[2]; + break; + } + case (2): { + p2d[0] = p3d[0]; + p2d[1] = p3d[1]; + break; + } + default: + BLI_assert(false); + } + return p2d; +} + +/** + Is a point in the interior of a 2d triangle or on one of its + * edges but not either endpoint of the edge? + * orient[pi][i] is the orientation test of the point pi against + * the side of the triangle starting at index i. + * Assume the triangle is non-degenerate and CCW-oriented. + * Then answer is true if p is left of or on all three of triangle a's edges, + * and strictly left of at least on of them. + */ +static bool non_trivially_2d_point_in_tri(const int orients[3][3], int pi) +{ + int p_left_01 = orients[pi][0]; + int p_left_12 = orients[pi][1]; + int p_left_20 = orients[pi][2]; + return (p_left_01 >= 0 && p_left_12 >= 0 && p_left_20 >= 0 && + (p_left_01 + p_left_12 + p_left_20) >= 2); +} + +/** + * Given orients as defined in non_trivially_2d_intersect, do the triangles + * overlap in a "hex" pattern? That is, the overlap region is a hexagon, which + * one gets by having, each point of one triangle being strictly right-of one + * edge of the other and strictly left of the other two edges; and vice versa. + */ +static bool non_trivially_2d_hex_overlap(int orients[2][3][3]) +{ + for (int ab = 0; ab < 2; ++ab) { + for (int i = 0; i < 3; ++i) { + bool ok = orients[ab][i][0] + orients[ab][i][1] + orients[ab][i][2] == 1 && + orients[ab][i][0] != 0 && orients[ab][i][1] != 0 && orients[i][2] != 0; + if (!ok) { + return false; + } + } + } + return true; +} + +/** + * Given orients as defined in non_trivially_2d_intersect, do the triangles + * have one shared edge in a "folded-over" configuration? + * As well as a shared edge, the third vertex of one triangle needs to be + * right-of one and left-of the other two edges of the other triangle. + */ +static bool non_trivially_2d_shared_edge_overlap(int orients[2][3][3], + const mpq2 *a[3], + const mpq2 *b[3]) +{ + for (int i = 0; i < 3; ++i) { + int in = (i + 1) % 3; + int inn = (i + 2) % 3; + for (int j = 0; j < 3; ++j) { + int jn = (j + 1) % 3; + int jnn = (j + 2) % 3; + if (*a[i] == *b[j] && *a[in] == *b[jn]) { + /* Edge from a[i] is shared with edge from b[j]. */ + /* See if a[inn] is right-of or on one of the other edges of b. + * If it is on, then it has to be right-of or left-of the shared edge, + * depending on which edge it is. */ + if (orients[0][inn][jn] < 0 || orients[0][inn][jnn] < 0) { + return true; + } + if (orients[0][inn][jn] == 0 && orients[0][inn][j] == 1) { + return true; + } + if (orients[0][inn][jnn] == 0 && orients[0][inn][j] == -1) { + return true; + } + /* Similarly for `b[jnn]`. */ + if (orients[1][jnn][in] < 0 || orients[1][jnn][inn] < 0) { + return true; + } + if (orients[1][jnn][in] == 0 && orients[1][jnn][i] == 1) { + return true; + } + if (orients[1][jnn][inn] == 0 && orients[1][jnn][i] == -1) { + return true; + } + } + } + } + return false; +} + +/** + * Are the triangles the same, perhaps with some permutation of vertices? + */ +static bool same_triangles(const mpq2 *a[3], const mpq2 *b[3]) +{ + for (int i = 0; i < 3; ++i) { + if (a[0] == b[i] && a[1] == b[(i + 1) % 3] && a[2] == b[(i + 2) % 3]) { + return true; + } + } + return false; +} + +/** + * Do 2d triangles (a[0], a[1], a[2]) and (b[0], b[1], b2[2]) intersect at more than just shared + * vertices or a shared edge? This is true if any point of one triangle is non-trivially inside the + * other. NO: that isn't quite sufficient: there is also the case where the verts are all mutually + * outside the other's triangle, but there is a hexagonal overlap region where they overlap. + */ +static bool non_trivially_2d_intersect(const mpq2 *a[3], const mpq2 *b[3]) +{ + /* TODO: Could experiment with trying bounding box tests before these. + * TODO: Find a less expensive way than 18 orient tests to do this. */ + + /* `orients[0][ai][bi]` is orient of point `a[ai]` compared to segment starting at `b[bi]`. + * `orients[1][bi][ai]` is orient of point `b[bi]` compared to segment starting at `a[ai]`. */ + int orients[2][3][3]; + for (int ab = 0; ab < 2; ++ab) { + for (int ai = 0; ai < 3; ++ai) { + for (int bi = 0; bi < 3; ++bi) { + if (ab == 0) { + orients[0][ai][bi] = orient2d(*b[bi], *b[(bi + 1) % 3], *a[ai]); + } + else { + orients[1][bi][ai] = orient2d(*a[ai], *a[(ai + 1) % 3], *b[bi]); + } + } + } + } + return non_trivially_2d_point_in_tri(orients[0], 0) || + non_trivially_2d_point_in_tri(orients[0], 1) || + non_trivially_2d_point_in_tri(orients[0], 2) || + non_trivially_2d_point_in_tri(orients[1], 0) || + non_trivially_2d_point_in_tri(orients[1], 1) || + non_trivially_2d_point_in_tri(orients[1], 2) || non_trivially_2d_hex_overlap(orients) || + non_trivially_2d_shared_edge_overlap(orients, a, b) || same_triangles(a, b); + return true; +} + +/** + * Does triangle t in tm non-trivially non-co-planar intersect any triangle + * in `CoplanarCluster cl`? Assume t is known to be in the same plane as all + * the triangles in cl, and that proj_axis is a good axis to project down + * to solve this problem in 2d. + */ +static bool non_trivially_coplanar_intersects(const IMesh &tm, + int t, + const CoplanarCluster &cl, + int proj_axis) +{ + const Face &tri = *tm.face(t); + mpq2 v0 = project_3d_to_2d(tri[0]->co_exact, proj_axis); + mpq2 v1 = project_3d_to_2d(tri[1]->co_exact, proj_axis); + mpq2 v2 = project_3d_to_2d(tri[2]->co_exact, proj_axis); + if (orient2d(v0, v1, v2) != 1) { + mpq2 tmp = v1; + v1 = v2; + v2 = tmp; + } + for (const int cl_t : cl) { + const Face &cl_tri = *tm.face(cl_t); + mpq2 ctv0 = project_3d_to_2d(cl_tri[0]->co_exact, proj_axis); + mpq2 ctv1 = project_3d_to_2d(cl_tri[1]->co_exact, proj_axis); + mpq2 ctv2 = project_3d_to_2d(cl_tri[2]->co_exact, proj_axis); + if (orient2d(ctv0, ctv1, ctv2) != 1) { + mpq2 tmp = ctv1; + ctv1 = ctv2; + ctv2 = tmp; + } + const mpq2 *v[] = {&v0, &v1, &v2}; + const mpq2 *ctv[] = {&ctv0, &ctv1, &ctv2}; + if (non_trivially_2d_intersect(v, ctv)) { + return true; + } + } + return false; +} + +/* Keeping this code for a while, but for now, almost all + * trivial intersects are found before calling intersect_tri_tri now. + */ +# if 0 +/** + * Do tri1 and tri2 intersect at all, and if so, is the intersection + * something other than a common vertex or a common edge? + * The \a itt value is the result of calling intersect_tri_tri on tri1, tri2. + */ +static bool non_trivial_intersect(const ITT_value &itt, const Face * tri1, const Face * tri2) +{ + if (itt.kind == INONE) { + return false; + } + const Face * tris[2] = {tri1, tri2}; + if (itt.kind == IPOINT) { + bool has_p_as_vert[2] {false, false}; + for (int i = 0; i < 2; ++i) { + for (const Vert * v : *tris[i]) { + if (itt.p1 == v->co_exact) { + has_p_as_vert[i] = true; + break; + } + } + } + return !(has_p_as_vert[0] && has_p_as_vert[1]); + } + if (itt.kind == ISEGMENT) { + bool has_seg_as_edge[2] = {false, false}; + for (int i = 0; i < 2; ++i) { + const Face &t = *tris[i]; + for (int pos : t.index_range()) { + int nextpos = t.next_pos(pos); + if ((itt.p1 == t[pos]->co_exact && itt.p2 == t[nextpos]->co_exact) || + (itt.p2 == t[pos]->co_exact && itt.p1 == t[nextpos]->co_exact)) { + has_seg_as_edge[i] = true; + break; + } + } + } + return !(has_seg_as_edge[0] && has_seg_as_edge[1]); + } + BLI_assert(itt.kind == ICOPLANAR); + /* TODO: refactor this common code with code above. */ + int proj_axis = mpq3::dominant_axis(tri1->plane.norm_exact); + mpq2 tri_2d[2][3]; + for (int i = 0; i < 2; ++i) { + mpq2 v0 = project_3d_to_2d((*tris[i])[0]->co_exact, proj_axis); + mpq2 v1 = project_3d_to_2d((*tris[i])[1]->co_exact, proj_axis); + mpq2 v2 = project_3d_to_2d((*tris[i])[2]->co_exact, proj_axis); + if (mpq2::orient2d(v0, v1, v2) != 1) { + mpq2 tmp = v1; + v1 = v2; + v2 = tmp; + } + tri_2d[i][0] = v0; + tri_2d[i][1] = v1; + tri_2d[i][2] = v2; + } + const mpq2 *va[] = {&tri_2d[0][0], &tri_2d[0][1], &tri_2d[0][2]}; + const mpq2 *vb[] = {&tri_2d[1][0], &tri_2d[1][1], &tri_2d[1][2]}; + return non_trivially_2d_intersect(va, vb); +} +# endif + +/** + * The sup and index functions are defined in the paper: + * EXACT GEOMETRIC COMPUTATION USING CASCADING, by + * Burnikel, Funke, and Seel. They are used to find absolute + * bounds on the error due to doing a calculation in double + * instead of exactly. For calculations involving only +, -, and *, + * the supremum is the same function except using absolute values + * on inputs and using + instead of -. + * The index function follows these rules: + * index(x op y) = 1 + max(index(x), index(y)) for op + or - + * index(x * y) = 1 + index(x) + index(y) + * index(x) = 0 if input x can be represented exactly as a double + * index(x) = 1 otherwise. + * + * With these rules in place, we know an absolute error bound: + * + * |E_exact - E| <= supremum(E) * index(E) * DBL_EPSILON + * + * where E_exact is what would have been the exact value of the + * expression and E is the one calculated with doubles. + * + * So the sign of E is the same as the sign of E_exact if + * |E| > supremum(E) * index(E) * DBL_EPSILON + * + * Note: a possible speedup would be to have a simple function + * that calculates the error bound if one knows that all values + * are less than some global maximum - most of the function would + * be calculated ahead of time. The global max could be passed + * from above. + */ +static double supremum_dot_cross(const double3 &a, const double3 &b) +{ + double3 abs_a = double3::abs(a); + double3 abs_b = double3::abs(b); + double3 c; + /* This is dot(cross(a, b), cross(a,b)) but using absolute values for a and b + * and always using + when operation is + or -. */ + c[0] = abs_a[1] * abs_b[2] + abs_a[2] * abs_b[1]; + c[1] = abs_a[2] * abs_b[0] + abs_a[0] * abs_b[2]; + c[2] = abs_a[0] * abs_b[1] + abs_a[1] * abs_b[0]; + return double3::dot(c, c); +} + +/** + * Used with supremum to get error bound. See Burnikel et al paper. + * index_plane_coord is the index of a plane coordinate calculated + * for a triangle using the usual formula, assuming the input + * coordinates have index 1. + * index_cross is the index of each coordinate of the cross product. + * It is actually 2 + 2 * (max index of input coords). + * index_dot_cross is the index of the dot product of two cross products. + * It is actually 7 + 4 * (max index of input coords) + */ +constexpr int index_dot_cross = 11; + +static double supremum_dot(const double3 &a, const double3 &b) +{ + double3 abs_a = double3::abs(a); + double3 abs_b = double3::abs(b); + return double3::dot(abs_a, abs_b); +} + +/* Actually index_dot = 3 + 2 * (max index of input coordinates). */ +/* The index of dot when inputs are plane_coords with index 1 is much higher. + * Plane coords have index 6. + */ +constexpr int index_dot_plane_coords = 15; + +static double supremum_orient3d(const double3 &a, + const double3 &b, + const double3 &c, + const double3 &d) +{ + double3 abs_a = double3::abs(a); + double3 abs_b = double3::abs(b); + double3 abs_c = double3::abs(c); + double3 abs_d = double3::abs(d); + double adx = abs_a[0] + abs_d[0]; + double bdx = abs_b[0] + abs_d[0]; + double cdx = abs_c[0] + abs_d[0]; + double ady = abs_a[1] + abs_d[1]; + double bdy = abs_b[1] + abs_d[1]; + double cdy = abs_c[1] + abs_d[1]; + double adz = abs_a[2] + abs_d[2]; + double bdz = abs_b[2] + abs_d[2]; + double cdz = abs_c[2] + abs_d[2]; + + double bdxcdy = bdx * cdy; + double cdxbdy = cdx * bdy; + + double cdxady = cdx * ady; + double adxcdy = adx * cdy; + + double adxbdy = adx * bdy; + double bdxady = bdx * ady; + + double det = adz * (bdxcdy + cdxbdy) + bdz * (cdxady + adxcdy) + cdz * (adxbdy + bdxady); + return det; +} + +/** Actually index_orient3d = 10 + 4 * (max degree of input coordinates) */ +constexpr int index_orient3d = 14; + +/** + * Return the approximate orient3d of the four double3's, with + * the guarantee that if the value is -1 or 1 then the underlying + * mpq3 test would also have returned that value. + * When the return value is 0, we are not sure of the sign. + */ +static int filter_orient3d(const double3 &a, const double3 &b, const double3 &c, const double3 &d) +{ + double o3dfast = orient3d_fast(a, b, c, d); + if (o3dfast == 0.0) { + return 0; + } + double err_bound = supremum_orient3d(a, b, c, d) * index_orient3d * DBL_EPSILON; + if (fabs(o3dfast) > err_bound) { + return o3dfast > 0.0 ? 1 : -1; + } + return 0; +} + +/** + * Return the approximate orient3d of the triangle plane points and v, with + * the guarantee that if the value is -1 or 1 then the underlying + * mpq3 test would also have returned that value. + * When the return value is 0, we are not sure of the sign. + */ +static int filter_tri_plane_vert_orient3d(const Face &tri, const Vert *v) +{ + return filter_orient3d(tri[0]->co, tri[1]->co, tri[2]->co, v->co); +} + +/** + * Are vectors a and b parallel or nearly parallel? + * This routine should only return false if we are certain + * that they are not parallel, taking into account the + * possible numeric errors and input value approximation. + */ +static bool near_parallel_vecs(const double3 &a, const double3 &b) +{ + double3 cr = double3::cross_high_precision(a, b); + double cr_len_sq = cr.length_squared(); + if (cr_len_sq == 0.0) { + return true; + } + double err_bound = supremum_dot_cross(a, b) * index_dot_cross * DBL_EPSILON; + if (cr_len_sq > err_bound) { + return false; + } + return true; +} + +/** + * Return true if we are sure that dot(a,b) > 0, taking into + * account the error bounds due to numeric errors and input value + * approximation. + */ +static bool dot_must_be_positive(const double3 &a, const double3 &b) +{ + double d = double3::dot(a, b); + if (d <= 0.0) { + return false; + } + double err_bound = supremum_dot(a, b) * index_dot_plane_coords * DBL_EPSILON; + if (d > err_bound) { + return true; + } + return false; +} + +/** + * Return the approximate side of point p on a plane with normal plane_no and point plane_p. + * The answer will be 1 if p is definitely above the plane, -1 if it is definitely below. + * If the answer is 0, we are unsure about which side of the plane (or if it is on the plane). + * In exact arithmetic, the answer is just `sgn(dot(p - plane_p, plane_no))`. + * + * The plane_no input is constructed, so has a higher index. + */ +constexpr int index_plane_side = 3 + 2 * index_dot_plane_coords; + +static int filter_plane_side(const double3 &p, + const double3 &plane_p, + const double3 &plane_no, + const double3 &abs_p, + const double3 &abs_plane_p, + const double3 &abs_plane_no) +{ + double d = double3::dot(p - plane_p, plane_no); + if (d == 0.0) { + return 0; + } + double supremum = double3::dot(abs_p + abs_plane_p, abs_plane_no); + double err_bound = supremum * index_plane_side * DBL_EPSILON; + if (d > err_bound) { + return d > 0 ? 1 : -1; + } + return 0; +} + +/** + * A fast, non-exhaustive test for non_trivial intersection. + * If this returns false then we are sure that tri1 and tri2 + * do not intersect. If it returns true, they may or may not + * non-trivially intersect. + * We assume that bounding box overlap tests have already been + * done, so don't repeat those here. This routine is checking + * for the very common cases (when doing mesh self-intersect) + * where triangles share an edge or a vertex, but don't + * otherwise intersect. + */ +static bool may_non_trivially_intersect(Face *t1, Face *t2) +{ + Face &tri1 = *t1; + Face &tri2 = *t2; + BLI_assert(t1->plane_populated() && t2->plane_populated()); + Face::FacePos share1_pos[3]; + Face::FacePos share2_pos[3]; + int n_shared = 0; + for (Face::FacePos p1 = 0; p1 < 3; ++p1) { + const Vert *v1 = tri1[p1]; + for (Face::FacePos p2 = 0; p2 < 3; ++p2) { + const Vert *v2 = tri2[p2]; + if (v1 == v2) { + share1_pos[n_shared] = p1; + share2_pos[n_shared] = p2; + ++n_shared; + } + } + } + if (n_shared == 2) { + /* t1 and t2 share an entire edge. + * If their normals are not parallel, they cannot non-trivially intersect. */ + if (!near_parallel_vecs(tri1.plane->norm, tri2.plane->norm)) { + return false; + } + /* The normals are parallel or nearly parallel. + * If the normals are in the same direction and the edges have opposite + * directions in the two triangles, they cannot non-trivially intersect. */ + bool erev1 = tri1.prev_pos(share1_pos[0]) == share1_pos[1]; + bool erev2 = tri2.prev_pos(share2_pos[0]) == share2_pos[1]; + if (erev1 != erev2) { + if (dot_must_be_positive(tri1.plane->norm, tri2.plane->norm)) { + return false; + } + } + } + else if (n_shared == 1) { + /* t1 and t2 share a vertex, but not an entire edge. + * If the two non-shared verts of t2 are both on the same + * side of tri1's plane, then they cannot non-trivially intersect. + * (There are some other cases that could be caught here but + * they are more expensive to check). */ + Face::FacePos p = share2_pos[0]; + const Vert *v2a = p == 0 ? tri2[1] : tri2[0]; + const Vert *v2b = (p == 0 || p == 1) ? tri2[2] : tri2[1]; + int o1 = filter_tri_plane_vert_orient3d(tri1, v2a); + int o2 = filter_tri_plane_vert_orient3d(tri1, v2b); + if (o1 == o2 && o1 != 0) { + return false; + } + p = share1_pos[0]; + const Vert *v1a = p == 0 ? tri1[1] : tri1[0]; + const Vert *v1b = (p == 0 || p == 1) ? tri1[2] : tri1[1]; + o1 = filter_tri_plane_vert_orient3d(tri2, v1a); + o2 = filter_tri_plane_vert_orient3d(tri2, v1b); + if (o1 == o2 && o1 != 0) { + return false; + } + } + /* We weren't able to prove that any intersection is trivial. */ + return true; +} + +/* + * interesect_tri_tri and helper functions. + * This code uses the algorithm of Guigue and Devillers, as described + * in "Faster Triangle-Triangle Intersection Tests". + * Adapted from github code by Eric Haines: + * github.com/erich666/jgt-code/tree/master/Volume_08/Number_1/Guigue2003 + */ + +/** + * Return the point on ab where the plane with normal n containing point c intersects it. + * Assumes ab is not perpendicular to n. + * This works because the ratio of the projections of ab and ac onto n is the same as + * the ratio along the line ab of the intersection point to the whole of ab. + */ +static inline mpq3 tti_interp(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &n) +{ + mpq3 ab = a - b; + mpq_class den = mpq3::dot(ab, n); + BLI_assert(den != 0); + mpq_class alpha = mpq3::dot(a - c, n) / den; + return a - alpha * ab; +} + +/** + * Return +1, 0, -1 as a + ad is above, on, or below the oriented plane containing a, b, c in CCW + * order. This is the same as -oriented(a, b, c, a + ad), but uses fewer arithmetic operations. + * TODO: change arguments to `const Vert *` and use floating filters. + */ +static inline int tti_above(const mpq3 &a, const mpq3 &b, const mpq3 &c, const mpq3 &ad) +{ + mpq3 n = mpq3::cross(b - a, c - a); + return sgn(mpq3::dot(ad, n)); +} + +/** + * Given that triangles (p1, q1, r1) and (p2, q2, r2) are in canonical order, + * use the classification chart in the Guigue and Devillers paper to find out + * how the intervals [i,j] and [k,l] overlap, where [i,j] is where p1r1 and p1q1 + * intersect the plane-plane intersection line, L, and [k,l] is where p2q2 and p2r2 + * intersect L. By the canonicalization, those segments intersect L exactly once. + * Canonicalization has made it so that for p1, q1, r1, either: + * (a)) p1 is off the second triangle's plane and both q1 and r1 are either + * on the plane or on the other side of it from p1; or + * (b) p1 is on the plane both q1 and r1 are on the same side + * of the plane and at least one of q1 and r1 are off the plane. + * Similarly for p2, q2, r2 with respect to the first triangle's plane. + */ +static ITT_value itt_canon2(const mpq3 &p1, + const mpq3 &q1, + const mpq3 &r1, + const mpq3 &p2, + const mpq3 &q2, + const mpq3 &r2, + const mpq3 &n1, + const mpq3 &n2) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\ntri_tri_intersect_canon:\n"; + std::cout << "p1=" << p1 << " q1=" << q1 << " r1=" << r1 << "\n"; + std::cout << "p2=" << p2 << " q2=" << q2 << " r2=" << r2 << "\n"; + std::cout << "n1=" << n1 << " n2=" << n2 << "\n"; + std::cout << "approximate values:\n"; + std::cout << "p1=(" << p1[0].get_d() << "," << p1[1].get_d() << "," << p1[2].get_d() << ")\n"; + std::cout << "q1=(" << q1[0].get_d() << "," << q1[1].get_d() << "," << q1[2].get_d() << ")\n"; + std::cout << "r1=(" << r1[0].get_d() << "," << r1[1].get_d() << "," << r1[2].get_d() << ")\n"; + std::cout << "p2=(" << p2[0].get_d() << "," << p2[1].get_d() << "," << p2[2].get_d() << ")\n"; + std::cout << "q2=(" << q2[0].get_d() << "," << q2[1].get_d() << "," << q2[2].get_d() << ")\n"; + std::cout << "r2=(" << r2[0].get_d() << "," << r2[1].get_d() << "," << r2[2].get_d() << ")\n"; + std::cout << "n1=(" << n1[0].get_d() << "," << n1[1].get_d() << "," << n1[2].get_d() << ")\n"; + std::cout << "n2=(" << n2[0].get_d() << "," << n2[1].get_d() << "," << n2[2].get_d() << ")\n"; + } + mpq3 p1p2 = p2 - p1; + mpq3 intersect_1; + mpq3 intersect_2; + bool no_overlap = false; + /* Top test in classification tree. */ + if (tti_above(p1, q1, r2, p1p2) > 0) { + /* Middle right test in classification tree. */ + if (tti_above(p1, r1, r2, p1p2) <= 0) { + /* Bottom right test in classification tree. */ + if (tti_above(p1, r1, q2, p1p2) > 0) { + /* Overlap is [k [i l] j]. */ + if (dbg_level > 0) { + std::cout << "overlap [k [i l] j]\n"; + } + /* i is intersect with p1r1. l is intersect with p2r2. */ + intersect_1 = tti_interp(p1, r1, p2, n2); + intersect_2 = tti_interp(p2, r2, p1, n1); + } + else { + /* Overlap is [i [k l] j]. */ + if (dbg_level > 0) { + std::cout << "overlap [i [k l] j]\n"; + } + /* k is intersect with p2q2. l is intersect is p2r2. */ + intersect_1 = tti_interp(p2, q2, p1, n1); + intersect_2 = tti_interp(p2, r2, p1, n1); + } + } + else { + /* No overlap: [k l] [i j]. */ + if (dbg_level > 0) { + std::cout << "no overlap: [k l] [i j]\n"; + } + no_overlap = true; + } + } + else { + /* Middle left test in classification tree. */ + if (tti_above(p1, q1, q2, p1p2) < 0) { + /* No overlap: [i j] [k l]. */ + if (dbg_level > 0) { + std::cout << "no overlap: [i j] [k l]\n"; + } + no_overlap = true; + } + else { + /* Bottom left test in classification tree. */ + if (tti_above(p1, r1, q2, p1p2) >= 0) { + /* Overlap is [k [i j] l]. */ + if (dbg_level > 0) { + std::cout << "overlap [k [i j] l]\n"; + } + /* i is intersect with p1r1. j is intersect with p1q1. */ + intersect_1 = tti_interp(p1, r1, p2, n2); + intersect_2 = tti_interp(p1, q1, p2, n2); + } + else { + /* Overlap is [i [k j] l]. */ + if (dbg_level > 0) { + std::cout << "overlap [i [k j] l]\n"; + } + /* k is intersect with p2q2. j is intersect with p1q1. */ + intersect_1 = tti_interp(p2, q2, p1, n1); + intersect_2 = tti_interp(p1, q1, p2, n2); + } + } + } + if (no_overlap) { + return ITT_value(INONE); + } + if (intersect_1 == intersect_2) { + if (dbg_level > 0) { + std::cout << "single intersect: " << intersect_1 << "\n"; + } + return ITT_value(IPOINT, intersect_1); + } + if (dbg_level > 0) { + std::cout << "intersect segment: " << intersect_1 << ", " << intersect_2 << "\n"; + } + return ITT_value(ISEGMENT, intersect_1, intersect_2); +} + +/* Helper function for intersect_tri_tri. Args have been canonicalized for triangle 1. */ + +static ITT_value itt_canon1(const mpq3 &p1, + const mpq3 &q1, + const mpq3 &r1, + const mpq3 &p2, + const mpq3 &q2, + const mpq3 &r2, + const mpq3 &n1, + const mpq3 &n2, + int sp2, + int sq2, + int sr2) +{ + constexpr int dbg_level = 0; + if (sp2 > 0) { + if (sq2 > 0) { + return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2); + } + if (sr2 > 0) { + return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2); + } + return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2); + } + if (sp2 < 0) { + if (sq2 < 0) { + return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2); + } + if (sr2 < 0) { + return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2); + } + return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2); + } + if (sq2 < 0) { + if (sr2 >= 0) { + return itt_canon2(p1, r1, q1, q2, r2, p2, n1, n2); + } + return itt_canon2(p1, q1, r1, p2, q2, r2, n1, n2); + } + if (sq2 > 0) { + if (sr2 > 0) { + return itt_canon2(p1, r1, q1, p2, q2, r2, n1, n2); + } + return itt_canon2(p1, q1, r1, q2, r2, p2, n1, n2); + } + if (sr2 > 0) { + return itt_canon2(p1, q1, r1, r2, p2, q2, n1, n2); + } + if (sr2 < 0) { + return itt_canon2(p1, r1, q1, r2, p2, q2, n1, n2); + } + if (dbg_level > 0) { + std::cout << "triangles are co-planar\n"; + } + return ITT_value(ICOPLANAR); +} + +static ITT_value intersect_tri_tri(const IMesh &tm, int t1, int t2) +{ + constexpr int dbg_level = 0; +# ifdef PERFDEBUG + incperfcount(1); /* Intersect_tri_tri calls. */ +# endif + const Face &tri1 = *tm.face(t1); + const Face &tri2 = *tm.face(t2); + BLI_assert(tri1.plane_populated() && tri2.plane_populated()); + const Vert *vp1 = tri1[0]; + const Vert *vq1 = tri1[1]; + const Vert *vr1 = tri1[2]; + const Vert *vp2 = tri2[0]; + const Vert *vq2 = tri2[1]; + const Vert *vr2 = tri2[2]; + if (dbg_level > 0) { + std::cout << "\nINTERSECT_TRI_TRI t1=" << t1 << ", t2=" << t2 << "\n"; + std::cout << " p1 = " << vp1 << "\n"; + std::cout << " q1 = " << vq1 << "\n"; + std::cout << " r1 = " << vr1 << "\n"; + std::cout << " p2 = " << vp2 << "\n"; + std::cout << " q2 = " << vq2 << "\n"; + std::cout << " r2 = " << vr2 << "\n"; + } + + /* Get signs of t1's vertices' distances to plane of t2 and vice versa. */ + + /* Try first getting signs with double arithmetic, with error bounds. + * If the signs calculated in this section are not 0, they are the same + * as what they would be using exact arithmetic. */ + const double3 &d_p1 = vp1->co; + const double3 &d_q1 = vq1->co; + const double3 &d_r1 = vr1->co; + const double3 &d_p2 = vp2->co; + const double3 &d_q2 = vq2->co; + const double3 &d_r2 = vr2->co; + const double3 &d_n2 = tri2.plane->norm; + + const double3 &abs_d_p1 = double3::abs(d_p1); + const double3 &abs_d_q1 = double3::abs(d_q1); + const double3 &abs_d_r1 = double3::abs(d_r1); + const double3 &abs_d_r2 = double3::abs(d_r2); + const double3 &abs_d_n2 = double3::abs(d_n2); + + int sp1 = filter_plane_side(d_p1, d_r2, d_n2, abs_d_p1, abs_d_r2, abs_d_n2); + int sq1 = filter_plane_side(d_q1, d_r2, d_n2, abs_d_q1, abs_d_r2, abs_d_n2); + int sr1 = filter_plane_side(d_r1, d_r2, d_n2, abs_d_r1, abs_d_r2, abs_d_n2); + if ((sp1 > 0 && sq1 > 0 && sr1 > 0) || (sp1 < 0 && sq1 < 0 && sr1 < 0)) { +# ifdef PERFDEBUG + incperfcount(2); /* Tri tri intersects decided by filter plane tests. */ +# endif + if (dbg_level > 0) { + std::cout << "no intersection, all t1's verts above or below t2\n"; + } + return ITT_value(INONE); + } + + const double3 &d_n1 = tri1.plane->norm; + const double3 &abs_d_p2 = double3::abs(d_p2); + const double3 &abs_d_q2 = double3::abs(d_q2); + const double3 &abs_d_n1 = double3::abs(d_n1); + + int sp2 = filter_plane_side(d_p2, d_r1, d_n1, abs_d_p2, abs_d_r1, abs_d_n1); + int sq2 = filter_plane_side(d_q2, d_r1, d_n1, abs_d_q2, abs_d_r1, abs_d_n1); + int sr2 = filter_plane_side(d_r2, d_r1, d_n1, abs_d_r2, abs_d_r1, abs_d_n1); + if ((sp2 > 0 && sq2 > 0 && sr2 > 0) || (sp2 < 0 && sq2 < 0 && sr2 < 0)) { +# ifdef PERFDEBUG + incperfcount(2); /* Tri tri intersects decided by filter plane tests. */ +# endif + if (dbg_level > 0) { + std::cout << "no intersection, all t2's verts above or below t1\n"; + } + return ITT_value(INONE); + } + + const mpq3 &p1 = vp1->co_exact; + const mpq3 &q1 = vq1->co_exact; + const mpq3 &r1 = vr1->co_exact; + const mpq3 &p2 = vp2->co_exact; + const mpq3 &q2 = vq2->co_exact; + const mpq3 &r2 = vr2->co_exact; + + const mpq3 &n2 = tri2.plane->norm_exact; + if (sp1 == 0) { + sp1 = sgn(mpq3::dot(p1 - r2, n2)); + } + if (sq1 == 0) { + sq1 = sgn(mpq3::dot(q1 - r2, n2)); + } + if (sr1 == 0) { + sr1 = sgn(mpq3::dot(r1 - r2, n2)); + } + + if (dbg_level > 1) { + std::cout << " sp1=" << sp1 << " sq1=" << sq1 << " sr1=" << sr1 << "\n"; + } + + if ((sp1 * sq1 > 0) && (sp1 * sr1 > 0)) { + if (dbg_level > 0) { + std::cout << "no intersection, all t1's verts above or below t2 (exact)\n"; + } +# ifdef PERFDEBUG + incperfcount(3); /* Tri tri intersects decided by exact plane tests. */ +# endif + return ITT_value(INONE); + } + + /* Repeat for signs of t2's vertices with respect to plane of t1. */ + const mpq3 &n1 = tri1.plane->norm_exact; + if (sp2 == 0) { + sp2 = sgn(mpq3::dot(p2 - r1, n1)); + } + if (sq2 == 0) { + sq2 = sgn(mpq3::dot(q2 - r1, n1)); + } + if (sr2 == 0) { + sr2 = sgn(mpq3::dot(r2 - r1, n1)); + } + + if (dbg_level > 1) { + std::cout << " sp2=" << sp2 << " sq2=" << sq2 << " sr2=" << sr2 << "\n"; + } + + if ((sp2 * sq2 > 0) && (sp2 * sr2 > 0)) { + if (dbg_level > 0) { + std::cout << "no intersection, all t2's verts above or below t1 (exact)\n"; + } +# ifdef PERFDEBUG + incperfcount(3); /* Tri tri intersects decided by exact plane tests. */ +# endif + return ITT_value(INONE); + } + + /* Do rest of the work with vertices in a canonical order, where p1 is on + * positive side of plane and q1, r1 are not, or p1 is on the plane and + * q1 and r1 are off the plane on the same side. */ + ITT_value ans; + if (sp1 > 0) { + if (sq1 > 0) { + ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else if (sr1 > 0) { + ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else { + ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + } + else if (sp1 < 0) { + if (sq1 < 0) { + ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + else if (sr1 < 0) { + ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + else { + ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + } + else { + if (sq1 < 0) { + if (sr1 >= 0) { + ans = itt_canon1(q1, r1, p1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else { + ans = itt_canon1(p1, q1, r1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + } + else if (sq1 > 0) { + if (sr1 > 0) { + ans = itt_canon1(p1, q1, r1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else { + ans = itt_canon1(q1, r1, p1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + } + else { + if (sr1 > 0) { + ans = itt_canon1(r1, p1, q1, p2, q2, r2, n1, n2, sp2, sq2, sr2); + } + else if (sr1 < 0) { + ans = itt_canon1(r1, p1, q1, p2, r2, q2, n1, n2, sp2, sr2, sq2); + } + else { + if (dbg_level > 0) { + std::cout << "triangles are co-planar\n"; + } + ans = ITT_value(ICOPLANAR); + } + } + } + if (ans.kind == ICOPLANAR) { + ans.t_source = t2; + } + +# ifdef PERFDEBUG + if (ans.kind != INONE) { + incperfcount(4); + } +# endif + return ans; +} + +struct CDT_data { + const Plane *t_plane; + Vector<mpq2> vert; + Vector<std::pair<int, int>> edge; + Vector<Vector<int>> face; + /** Parallels face, gives id from input #IMesh of input face. */ + Vector<int> input_face; + /** Parallels face, says if input face orientation is opposite. */ + Vector<bool> is_reversed; + /** Result of running CDT on input with (vert, edge, face). */ + CDT_result<mpq_class> cdt_out; + int proj_axis; +}; + +/** + * We could de-duplicate verts here, but CDT routine will do that anyway. + */ +static int prepare_need_vert(CDT_data &cd, const mpq3 &p3d) +{ + mpq2 p2d = project_3d_to_2d(p3d, cd.proj_axis); + int v = cd.vert.append_and_get_index(p2d); + return v; +} + +/** + * To un-project a 2d vert that was projected along cd.proj_axis, we copy the coordinates + * from the two axes not involved in the projection, and use the plane equation of the + * originating 3d plane, cd.t_plane, to derive the coordinate of the projected axis. + * The plane equation says a point p is on the plane if dot(p, plane.n()) + plane.d() == 0. + * Assume that the projection axis is such that plane.n()[proj_axis] != 0. + */ +static mpq3 unproject_cdt_vert(const CDT_data &cd, const mpq2 &p2d) +{ + mpq3 p3d; + BLI_assert(cd.t_plane->exact_populated()); + BLI_assert(cd.t_plane->norm_exact[cd.proj_axis] != 0); + const mpq3 &n = cd.t_plane->norm_exact; + const mpq_class &d = cd.t_plane->d_exact; + switch (cd.proj_axis) { + case (0): { + mpq_class num = n[1] * p2d[0] + n[2] * p2d[1] + d; + num = -num; + p3d[0] = num / n[0]; + p3d[1] = p2d[0]; + p3d[2] = p2d[1]; + break; + } + case (1): { + p3d[0] = p2d[0]; + mpq_class num = n[0] * p2d[0] + n[2] * p2d[1] + d; + num = -num; + p3d[1] = num / n[1]; + p3d[2] = p2d[1]; + break; + } + case (2): { + p3d[0] = p2d[0]; + p3d[1] = p2d[1]; + mpq_class num = n[0] * p2d[0] + n[1] * p2d[1] + d; + num = -num; + p3d[2] = num / n[2]; + break; + } + default: + BLI_assert(false); + } + return p3d; +} + +static void prepare_need_edge(CDT_data &cd, const mpq3 &p1, const mpq3 &p2) +{ + int v1 = prepare_need_vert(cd, p1); + int v2 = prepare_need_vert(cd, p2); + cd.edge.append(std::pair<int, int>(v1, v2)); +} + +static void prepare_need_tri(CDT_data &cd, const IMesh &tm, int t) +{ + const Face &tri = *tm.face(t); + int v0 = prepare_need_vert(cd, tri[0]->co_exact); + int v1 = prepare_need_vert(cd, tri[1]->co_exact); + int v2 = prepare_need_vert(cd, tri[2]->co_exact); + bool rev; + /* How to get CCW orientation of projected triangle? Note that when look down y axis + * as opposed to x or z, the orientation of the other two axes is not right-and-up. */ + BLI_assert(cd.t_plane->exact_populated()); + if (tri.plane->norm_exact[cd.proj_axis] >= 0) { + rev = cd.proj_axis == 1; + } + else { + rev = cd.proj_axis != 1; + } + int cd_t = cd.face.append_and_get_index(Vector<int>()); + cd.face[cd_t].append(v0); + if (rev) { + cd.face[cd_t].append(v2); + cd.face[cd_t].append(v1); + } + else { + cd.face[cd_t].append(v1); + cd.face[cd_t].append(v2); + } + cd.input_face.append(t); + cd.is_reversed.append(rev); +} + +static CDT_data prepare_cdt_input(const IMesh &tm, int t, const Vector<ITT_value> itts) +{ + CDT_data ans; + BLI_assert(tm.face(t)->plane_populated()); + ans.t_plane = tm.face(t)->plane; + BLI_assert(ans.t_plane->exact_populated()); + ans.proj_axis = mpq3::dominant_axis(ans.t_plane->norm_exact); + prepare_need_tri(ans, tm, t); + for (const ITT_value &itt : itts) { + switch (itt.kind) { + case INONE: + break; + case IPOINT: { + prepare_need_vert(ans, itt.p1); + break; + } + case ISEGMENT: { + prepare_need_edge(ans, itt.p1, itt.p2); + break; + } + case ICOPLANAR: { + prepare_need_tri(ans, tm, itt.t_source); + break; + } + } + } + return ans; +} + +static CDT_data prepare_cdt_input_for_cluster(const IMesh &tm, + const CoplanarClusterInfo &clinfo, + int c, + const Vector<ITT_value> itts) +{ + CDT_data ans; + BLI_assert(c < clinfo.tot_cluster()); + const CoplanarCluster &cl = clinfo.cluster(c); + BLI_assert(cl.tot_tri() > 0); + int t0 = cl.tri(0); + BLI_assert(tm.face(t0)->plane_populated()); + ans.t_plane = tm.face(t0)->plane; + BLI_assert(ans.t_plane->exact_populated()); + ans.proj_axis = mpq3::dominant_axis(ans.t_plane->norm_exact); + for (const int t : cl) { + prepare_need_tri(ans, tm, t); + } + for (const ITT_value &itt : itts) { + switch (itt.kind) { + case IPOINT: { + prepare_need_vert(ans, itt.p1); + break; + } + case ISEGMENT: { + prepare_need_edge(ans, itt.p1, itt.p2); + break; + } + default: + break; + } + } + return ans; +} + +/** + * Fills in cd.cdt_out with result of doing the cdt calculation on (vert, edge, face). + */ +static void do_cdt(CDT_data &cd) +{ + constexpr int dbg_level = 0; + CDT_input<mpq_class> cdt_in; + cdt_in.vert = Span<mpq2>(cd.vert); + cdt_in.edge = Span<std::pair<int, int>>(cd.edge); + cdt_in.face = Span<Vector<int>>(cd.face); + if (dbg_level > 0) { + std::cout << "CDT input\nVerts:\n"; + for (int i : cdt_in.vert.index_range()) { + std::cout << "v" << i << ": " << cdt_in.vert[i] << "=(" << cdt_in.vert[i][0].get_d() << "," + << cdt_in.vert[i][1].get_d() << ")\n"; + } + std::cout << "Edges:\n"; + for (int i : cdt_in.edge.index_range()) { + std::cout << "e" << i << ": (" << cdt_in.edge[i].first << ", " << cdt_in.edge[i].second + << ")\n"; + } + std::cout << "Tris\n"; + for (int f : cdt_in.face.index_range()) { + std::cout << "f" << f << ": "; + for (int j : cdt_in.face[f].index_range()) { + std::cout << cdt_in.face[f][j] << " "; + } + std::cout << "\n"; + } + } + cdt_in.epsilon = 0; /* TODO: needs attention for non-exact T. */ + cd.cdt_out = blender::meshintersect::delaunay_2d_calc(cdt_in, CDT_INSIDE); + if (dbg_level > 0) { + std::cout << "\nCDT result\nVerts:\n"; + for (int i : cd.cdt_out.vert.index_range()) { + std::cout << "v" << i << ": " << cd.cdt_out.vert[i] << "=(" << cd.cdt_out.vert[i][0].get_d() + << "," << cd.cdt_out.vert[i][1].get_d() << "\n"; + } + std::cout << "Tris\n"; + for (int f : cd.cdt_out.face.index_range()) { + std::cout << "f" << f << ": "; + for (int j : cd.cdt_out.face[f].index_range()) { + std::cout << cd.cdt_out.face[f][j] << " "; + } + std::cout << "orig: "; + for (int j : cd.cdt_out.face_orig[f].index_range()) { + std::cout << cd.cdt_out.face_orig[f][j] << " "; + } + std::cout << "\n"; + } + std::cout << "Edges\n"; + for (int e : cd.cdt_out.edge.index_range()) { + std::cout << "e" << e << ": (" << cd.cdt_out.edge[e].first << ", " + << cd.cdt_out.edge[e].second << ") "; + std::cout << "orig: "; + for (int j : cd.cdt_out.edge_orig[e].index_range()) { + std::cout << cd.cdt_out.edge_orig[e][j] << " "; + } + std::cout << "\n"; + } + } +} + +static int get_cdt_edge_orig( + int i0, int i1, const CDT_data &cd, const IMesh &in_tm, bool *r_is_intersect) +{ + int foff = cd.cdt_out.face_edge_offset; + *r_is_intersect = false; + for (int e : cd.cdt_out.edge.index_range()) { + std::pair<int, int> edge = cd.cdt_out.edge[e]; + if ((edge.first == i0 && edge.second == i1) || (edge.first == i1 && edge.second == i0)) { + /* Pick an arbitrary orig, but not one equal to NO_INDEX, if we can help it. */ + /* TODO: if edge has origs from more than on part of the nary input, + * then want to set *r_is_intersect to true. */ + for (int orig_index : cd.cdt_out.edge_orig[e]) { + /* orig_index encodes the triangle and pos within the triangle of the input edge. */ + if (orig_index >= foff) { + int in_face_index = (orig_index / foff) - 1; + int pos = orig_index % foff; + /* We need to retrieve the edge orig field from the Face used to populate the + * in_face_index'th face of the CDT, at the pos'th position of the face. */ + int in_tm_face_index = cd.input_face[in_face_index]; + BLI_assert(in_tm_face_index < in_tm.face_size()); + const Face *facep = in_tm.face(in_tm_face_index); + BLI_assert(pos < facep->size()); + bool is_rev = cd.is_reversed[in_face_index]; + int eorig = is_rev ? facep->edge_orig[2 - pos] : facep->edge_orig[pos]; + if (eorig != NO_INDEX) { + return eorig; + } + } + else { + /* This edge came from an edge input to the CDT problem, + * so it is an intersect edge. */ + *r_is_intersect = true; + /* TODO: maybe there is an orig index: + * This happens if an input edge was formed by an input face having + * an edge that is co-planar with the cluster, while the face as a whole is not. */ + return NO_INDEX; + } + } + return NO_INDEX; + } + } + return NO_INDEX; +} + +/** + * Using the result of CDT in cd.cdt_out, extract an #IMesh representing the subdivision + * of input triangle t, which should be an element of cd.input_face. + */ +static IMesh extract_subdivided_tri(const CDT_data &cd, + const IMesh &in_tm, + int t, + IMeshArena *arena) +{ + const CDT_result<mpq_class> &cdt_out = cd.cdt_out; + int t_in_cdt = -1; + for (int i = 0; i < cd.input_face.size(); ++i) { + if (cd.input_face[i] == t) { + t_in_cdt = i; + } + } + if (t_in_cdt == -1) { + std::cout << "Could not find " << t << " in cdt input tris\n"; + BLI_assert(false); + return IMesh(); + } + int t_orig = in_tm.face(t)->orig; + constexpr int inline_buf_size = 20; + Vector<Face *, inline_buf_size> faces; + for (int f : cdt_out.face.index_range()) { + if (cdt_out.face_orig[f].contains(t_in_cdt)) { + BLI_assert(cdt_out.face[f].size() == 3); + int i0 = cdt_out.face[f][0]; + int i1 = cdt_out.face[f][1]; + int i2 = cdt_out.face[f][2]; + mpq3 v0co = unproject_cdt_vert(cd, cdt_out.vert[i0]); + mpq3 v1co = unproject_cdt_vert(cd, cdt_out.vert[i1]); + mpq3 v2co = unproject_cdt_vert(cd, cdt_out.vert[i2]); + /* No need to provide an original index: if coord matches + * an original one, then it will already be in the arena + * with the correct orig field. */ + const Vert *v0 = arena->add_or_find_vert(v0co, NO_INDEX); + const Vert *v1 = arena->add_or_find_vert(v1co, NO_INDEX); + const Vert *v2 = arena->add_or_find_vert(v2co, NO_INDEX); + Face *facep; + bool is_isect0; + bool is_isect1; + bool is_isect2; + if (cd.is_reversed[t_in_cdt]) { + int oe0 = get_cdt_edge_orig(i0, i2, cd, in_tm, &is_isect0); + int oe1 = get_cdt_edge_orig(i2, i1, cd, in_tm, &is_isect1); + int oe2 = get_cdt_edge_orig(i1, i0, cd, in_tm, &is_isect2); + facep = arena->add_face( + {v0, v2, v1}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2}); + } + else { + int oe0 = get_cdt_edge_orig(i0, i1, cd, in_tm, &is_isect0); + int oe1 = get_cdt_edge_orig(i1, i2, cd, in_tm, &is_isect1); + int oe2 = get_cdt_edge_orig(i2, i0, cd, in_tm, &is_isect2); + facep = arena->add_face( + {v0, v1, v2}, t_orig, {oe0, oe1, oe2}, {is_isect0, is_isect1, is_isect2}); + } + facep->populate_plane(false); + faces.append(facep); + } + } + return IMesh(faces); +} + +static IMesh extract_single_tri(const IMesh &tm, int t) +{ + Face *f = tm.face(t); + return IMesh({f}); +} + +static bool bvhtreeverlap_cmp(const BVHTreeOverlap &a, const BVHTreeOverlap &b) +{ + if (a.indexA < b.indexA) { + return true; + } + if ((a.indexA == b.indexA) & (a.indexB < b.indexB)) { + return true; + } + return false; +} +class TriOverlaps { + BVHTree *tree_{nullptr}; + BVHTree *tree_b_{nullptr}; + BVHTreeOverlap *overlap_{nullptr}; + uint overlap_tot_{0}; + + struct CBData { + const IMesh &tm; + std::function<int(int)> shape_fn; + int nshapes; + bool use_self; + }; + + public: + TriOverlaps(const IMesh &tm, + const Array<BoundingBox> &tri_bb, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self) + { + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "TriOverlaps construction\n"; + } + /* Tree type is 8 => octtree; axis = 6 => using XYZ axes only. */ + tree_ = BLI_bvhtree_new(tm.face_size(), FLT_EPSILON, 8, 6); + /* In the common case of a binary boolean and no self intersection in + * each shape, we will use two trees and simple bounding box overlap. */ + bool two_trees_no_self = nshapes == 2 && !use_self; + if (two_trees_no_self) { + tree_b_ = BLI_bvhtree_new(tm.face_size(), FLT_EPSILON, 8, 6); + } + float bbpts[6]; + for (int t : tm.face_index_range()) { + const BoundingBox &bb = tri_bb[t]; + copy_v3_v3(bbpts, bb.min); + copy_v3_v3(bbpts + 3, bb.max); + int shape = shape_fn(tm.face(t)->orig); + if (two_trees_no_self) { + if (shape == 0) { + BLI_bvhtree_insert(tree_, t, bbpts, 2); + } + else if (shape == 1) { + BLI_bvhtree_insert(tree_b_, t, bbpts, 2); + } + } + else { + if (shape != -1) { + BLI_bvhtree_insert(tree_, t, bbpts, 2); + } + } + } + BLI_bvhtree_balance(tree_); + if (two_trees_no_self) { + BLI_bvhtree_balance(tree_b_); + /* Don't expect a lot of trivial intersects in this case. */ + overlap_ = BLI_bvhtree_overlap(tree_, tree_b_, &overlap_tot_, NULL, NULL); + } + else { + CBData cbdata{tm, shape_fn, nshapes, use_self}; + if (nshapes == 1 && use_self) { + /* Expect a lot of trivial intersects from quads that are triangulated + * and faces that share vertices. + * Filter them out with a callback. */ + overlap_ = BLI_bvhtree_overlap( + tree_, tree_, &overlap_tot_, only_nontrivial_intersects, &cbdata); + } + else { + overlap_ = BLI_bvhtree_overlap( + tree_, tree_, &overlap_tot_, only_different_shapes, &cbdata); + } + } + /* The rest of the code is simpler and easier to parallelize if, in the two-trees case, + * we repeat the overlaps with indexA and indexB reversed. It is important that + * in the repeated part, sorting will then bring things with indexB together. */ + if (two_trees_no_self) { + overlap_ = static_cast<BVHTreeOverlap *>( + MEM_reallocN(overlap_, 2 * overlap_tot_ * sizeof(overlap_[0]))); + for (uint i = 0; i < overlap_tot_; ++i) { + overlap_[overlap_tot_ + i].indexA = overlap_[i].indexB; + overlap_[overlap_tot_ + i].indexB = overlap_[i].indexA; + } + overlap_tot_ += overlap_tot_; + } + /* Sort the overlaps to bring all the intersects with a given indexA together. */ + std::sort(overlap_, overlap_ + overlap_tot_, bvhtreeverlap_cmp); + if (dbg_level > 0) { + std::cout << overlap_tot_ << " overlaps found:\n"; + for (BVHTreeOverlap ov : overlap()) { + std::cout << "A: " << ov.indexA << ", B: " << ov.indexB << "\n"; + } + } + } + + ~TriOverlaps() + { + if (tree_) { + BLI_bvhtree_free(tree_); + } + if (tree_b_) { + BLI_bvhtree_free(tree_b_); + } + if (overlap_) { + MEM_freeN(overlap_); + } + } + + Span<BVHTreeOverlap> overlap() const + { + return Span<BVHTreeOverlap>(overlap_, overlap_tot_); + } + + private: + static bool only_nontrivial_intersects(void *userdata, + int index_a, + int index_b, + int UNUSED(thread)) + { + CBData *cbdata = static_cast<CBData *>(userdata); + return may_non_trivially_intersect(cbdata->tm.face(index_a), cbdata->tm.face(index_b)); + } + + static bool only_different_shapes(void *userdata, int index_a, int index_b, int UNUSED(thread)) + { + CBData *cbdata = static_cast<CBData *>(userdata); + return cbdata->tm.face(index_a)->orig != cbdata->tm.face(index_b)->orig; + } +}; + +/** + * Data needed for parallelization of #calc_overlap_itts. + */ +struct OverlapIttsData { + Vector<std::pair<int, int>> intersect_pairs; + Map<std::pair<int, int>, ITT_value> &itt_map; + const IMesh &tm; + IMeshArena *arena; + + OverlapIttsData(Map<std::pair<int, int>, ITT_value> &itt_map, const IMesh &tm, IMeshArena *arena) + : itt_map(itt_map), tm(tm), arena(arena) + { + } +}; + +/** + * Return a std::pair containing a and b in canonical order: + * With a <= b. + */ +static std::pair<int, int> canon_int_pair(int a, int b) +{ + if (a > b) { + std::swap(a, b); + } + return std::pair<int, int>(a, b); +} + +static void calc_overlap_itts_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + constexpr int dbg_level = 0; + OverlapIttsData *data = static_cast<OverlapIttsData *>(userdata); + std::pair<int, int> tri_pair = data->intersect_pairs[iter]; + int a = tri_pair.first; + int b = tri_pair.second; + if (dbg_level > 0) { + std::cout << "calc_overlap_itts_range_func a=" << a << ", b=" << b << "\n"; + } + ITT_value itt = intersect_tri_tri(data->tm, a, b); + if (dbg_level > 0) { + std::cout << "result of intersecting " << a << " and " << b << " = " << itt << "\n"; + } + BLI_assert(data->itt_map.contains(tri_pair)); + data->itt_map.add_overwrite(tri_pair, itt); +} + +/** + * Fill in itt_map with the vector of ITT_values that result from intersecting the triangles in ov. + * Use a canonical order for triangles: (a,b) where a < b. + * Don't bother doing this if both a and b are part of the same co-planar cluster, as the + * intersection for those will be handled by CDT, later. + */ +static void calc_overlap_itts(Map<std::pair<int, int>, ITT_value> &itt_map, + const IMesh &tm, + const CoplanarClusterInfo &clinfo, + const TriOverlaps &ov, + IMeshArena *arena) +{ + OverlapIttsData data(itt_map, tm, arena); + /* Put dummy values in `itt_map` initially, + * so map entries will exist when doing the range function. + * This means we won't have to protect the `itt_map.add_overwrite` function with a lock. */ + for (const BVHTreeOverlap &olap : ov.overlap()) { + std::pair<int, int> key = canon_int_pair(olap.indexA, olap.indexB); + if (!itt_map.contains(key)) { + int ca = clinfo.tri_cluster(key.first); + int cb = clinfo.tri_cluster(key.second); + if (ca == NO_INDEX || ca != cb) { + itt_map.add_new(key, ITT_value()); + data.intersect_pairs.append(key); + } + } + } + int tot_intersect_pairs = data.intersect_pairs.size(); + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 1000; + settings.use_threading = intersect_use_threading; + BLI_task_parallel_range(0, tot_intersect_pairs, &data, calc_overlap_itts_range_func, &settings); +} + +/** + * Data needed for parallelization of calc_subdivided_tris. + */ +struct OverlapTriRange { + int tri_index; + int overlap_start; + int len; +}; +struct SubdivideTrisData { + Array<IMesh> &r_tri_subdivided; + const IMesh &tm; + const Map<std::pair<int, int>, ITT_value> &itt_map; + Span<BVHTreeOverlap> overlap; + IMeshArena *arena; + + /* This vector gives, for each triangle in tm that has an intersection + * we want to calculate: what the index of that triangle in tm is, + * where it starts in the ov structure as indexA, and how many + * overlap pairs have that same indexA (they will be continuous). */ + Vector<OverlapTriRange> overlap_tri_range; + + SubdivideTrisData(Array<IMesh> &r_tri_subdivided, + const IMesh &tm, + const Map<std::pair<int, int>, ITT_value> &itt_map, + Span<BVHTreeOverlap> overlap, + IMeshArena *arena) + : r_tri_subdivided(r_tri_subdivided), + tm(tm), + itt_map(itt_map), + overlap(overlap), + arena(arena), + overlap_tri_range{} + { + } +}; + +static void calc_subdivided_tri_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + constexpr int dbg_level = 0; + SubdivideTrisData *data = static_cast<SubdivideTrisData *>(userdata); + OverlapTriRange &otr = data->overlap_tri_range[iter]; + int t = otr.tri_index; + if (dbg_level > 0) { + std::cout << "calc_subdivided_tri_range_func\nt=" << t << " start=" << otr.overlap_start + << " len=" << otr.len << "\n"; + } + constexpr int inline_capacity = 100; + Vector<ITT_value, inline_capacity> itts(otr.len); + for (int j = otr.overlap_start; j < otr.overlap_start + otr.len; ++j) { + int t_other = data->overlap[j].indexB; + std::pair<int, int> key = canon_int_pair(t, t_other); + ITT_value itt; + if (data->itt_map.contains(key)) { + itt = data->itt_map.lookup(key); + } + if (itt.kind != INONE) { + itts.append(itt); + } + if (dbg_level > 0) { + std::cout << " tri t" << t_other << "; result = " << itt << "\n"; + } + } + if (itts.size() > 0) { + CDT_data cd_data = prepare_cdt_input(data->tm, t, itts); + do_cdt(cd_data); + data->r_tri_subdivided[t] = extract_subdivided_tri(cd_data, data->tm, t, data->arena); + if (dbg_level > 0) { + std::cout << "subdivide output\n" << data->r_tri_subdivided[t]; + } + } +} + +/** + * For each triangle in tm, fill in the corresponding slot in + * r_tri_subdivided with the result of intersecting it with + * all the other triangles in the mesh, if it intersects any others. + * But don't do this for triangles that are part of a cluster. + * Also, do nothing here if the answer is just the triangle itself. + */ +static void calc_subdivided_tris(Array<IMesh> &r_tri_subdivided, + const IMesh &tm, + const Map<std::pair<int, int>, ITT_value> &itt_map, + const CoplanarClusterInfo &clinfo, + const TriOverlaps &ov, + IMeshArena *arena) +{ + const int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nCALC_SUBDIVIDED_TRIS\n\n"; + } + Span<BVHTreeOverlap> overlap = ov.overlap(); + SubdivideTrisData data(r_tri_subdivided, tm, itt_map, overlap, arena); + int overlap_tot = overlap.size(); + data.overlap_tri_range = Vector<OverlapTriRange>(); + data.overlap_tri_range.reserve(overlap_tot); + int overlap_index = 0; + while (overlap_index < overlap_tot) { + int t = overlap[overlap_index].indexA; + int i = overlap_index; + while (i + 1 < overlap_tot && overlap[i + 1].indexA == t) { + ++i; + } + /* Now overlap[overlap_index] to overlap[i] have indexA == t. + * We only record ranges for triangles that are not in clusters, + * because the ones in clusters are handled separately. */ + if (clinfo.tri_cluster(t) == NO_INDEX) { + int len = i - overlap_index + 1; + if (!(len == 1 && overlap[overlap_index].indexB == t)) { + OverlapTriRange range = {t, overlap_index, len}; + data.overlap_tri_range.append(range); +# ifdef PERFDEBUG + bumpperfcount(0, len); /* Non-cluster overlaps. */ +# endif + } + } + overlap_index = i + 1; + } + int overlap_tri_range_tot = data.overlap_tri_range.size(); + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.min_iter_per_thread = 50; + settings.use_threading = intersect_use_threading; + BLI_task_parallel_range( + 0, overlap_tri_range_tot, &data, calc_subdivided_tri_range_func, &settings); +} + +/* Get first index in ov where indexA == t. Assuming sorted on indexA. */ +static int find_first_overlap_index(const TriOverlaps &ov, int t) +{ + Span<BVHTreeOverlap> span = ov.overlap(); + if (span.size() == 0) { + return -1; + } + BVHTreeOverlap bo{t, -1}; + const BVHTreeOverlap *p = std::lower_bound( + span.begin(), span.end(), bo, [](const BVHTreeOverlap &o1, const BVHTreeOverlap &o2) { + return o1.indexA < o2.indexA; + }); + if (p != span.end()) { + return p - span.begin(); + } + return -1; +} + +static CDT_data calc_cluster_subdivided(const CoplanarClusterInfo &clinfo, + int c, + const IMesh &tm, + const TriOverlaps &ov, + const Map<std::pair<int, int>, ITT_value> &itt_map, + IMeshArena *UNUSED(arena)) +{ + constexpr int dbg_level = 0; + BLI_assert(c < clinfo.tot_cluster()); + const CoplanarCluster &cl = clinfo.cluster(c); + /* Make a CDT input with triangles from C and intersects from other triangles in tm. */ + if (dbg_level > 0) { + std::cout << "CALC_CLUSTER_SUBDIVIDED for cluster " << c << " = " << cl << "\n"; + } + /* Get vector itts of all intersections of a triangle of cl with any triangle of tm not + * in cl and not co-planar with it (for that latter, if there were an intersection, + * it should already be in cluster cl). */ + Vector<ITT_value> itts; + Span<BVHTreeOverlap> ovspan = ov.overlap(); + for (int t : cl) { + if (dbg_level > 0) { + std::cout << "find intersects with triangle " << t << " of cluster\n"; + } + int first_i = find_first_overlap_index(ov, t); + if (first_i == -1) { + continue; + } + for (int i = first_i; i < ovspan.size() && ovspan[i].indexA == t; ++i) { + int t_other = ovspan[i].indexB; + if (clinfo.tri_cluster(t_other) != c) { + if (dbg_level > 0) { + std::cout << "use intersect(" << t << "," << t_other << "\n"; + } + std::pair<int, int> key = canon_int_pair(t, t_other); + if (itt_map.contains(key)) { + ITT_value itt = itt_map.lookup(key); + if (itt.kind != INONE && itt.kind != ICOPLANAR) { + itts.append(itt); + if (dbg_level > 0) { + std::cout << " itt = " << itt << "\n"; + } + } + } + } + } + } + /* Use CDT to subdivide the cluster triangles and the points and segs in itts. */ + CDT_data cd_data = prepare_cdt_input_for_cluster(tm, clinfo, c, itts); + do_cdt(cd_data); + return cd_data; +} + +static IMesh union_tri_subdivides(const blender::Array<IMesh> &tri_subdivided) +{ + int tot_tri = 0; + for (const IMesh &m : tri_subdivided) { + tot_tri += m.face_size(); + } + Array<Face *> faces(tot_tri); + int face_index = 0; + for (const IMesh &m : tri_subdivided) { + for (Face *f : m.faces()) { + faces[face_index++] = f; + } + } + return IMesh(faces); +} + +static CoplanarClusterInfo find_clusters(const IMesh &tm, const Array<BoundingBox> &tri_bb) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "FIND_CLUSTERS\n"; + } + CoplanarClusterInfo ans(tm.face_size()); + /* There can be more than one #CoplanarCluster per plane. Accumulate them in + * a Vector. We will have to merge some elements of the Vector as we discover + * triangles that form intersection bridges between two or more clusters. */ + Map<Plane, Vector<CoplanarCluster>> plane_cls; + plane_cls.reserve(tm.face_size()); + for (int t : tm.face_index_range()) { + /* Use a canonical version of the plane for map index. + * We can't just store the canonical version in the face + * since canonicalizing loses the orientation of the normal. */ + Plane tplane = *tm.face(t)->plane; + BLI_assert(tplane.exact_populated()); + tplane.make_canonical(); + if (dbg_level > 0) { + std::cout << "plane for tri " << t << " = " << &tplane << "\n"; + } + /* Assume all planes are in canonical from (see canon_plane()). */ + if (plane_cls.contains(tplane)) { + Vector<CoplanarCluster> &curcls = plane_cls.lookup(tplane); + if (dbg_level > 0) { + std::cout << "already has " << curcls.size() << " clusters\n"; + } + int proj_axis = mpq3::dominant_axis(tplane.norm_exact); + /* Partition `curcls` into those that intersect t non-trivially, and those that don't. */ + Vector<CoplanarCluster *> int_cls; + Vector<CoplanarCluster *> no_int_cls; + for (CoplanarCluster &cl : curcls) { + if (bbs_might_intersect(tri_bb[t], cl.bounding_box()) && + non_trivially_coplanar_intersects(tm, t, cl, proj_axis)) { + if (dbg_level > 1) { + std::cout << "append to int_cls\n"; + } + int_cls.append(&cl); + } + else { + if (dbg_level > 1) { + std::cout << "append to no_int_cls\n"; + } + no_int_cls.append(&cl); + } + } + if (int_cls.size() == 0) { + /* t doesn't intersect any existing cluster in its plane, so make one just for it. */ + if (dbg_level > 1) { + std::cout << "no intersecting clusters for t, make a new one\n"; + } + curcls.append(CoplanarCluster(t, tri_bb[t])); + } + else if (int_cls.size() == 1) { + /* t intersects exactly one existing cluster, so can add t to that cluster. */ + if (dbg_level > 1) { + std::cout << "exactly one existing cluster, " << int_cls[0] << ", adding to it\n"; + } + int_cls[0]->add_tri(t, tri_bb[t]); + } + else { + /* t intersections 2 or more existing clusters: need to merge them and replace all the + * originals with the merged one in `curcls`. */ + if (dbg_level > 1) { + std::cout << "merging\n"; + } + CoplanarCluster mergecl; + mergecl.add_tri(t, tri_bb[t]); + for (CoplanarCluster *cl : int_cls) { + for (int t : *cl) { + mergecl.add_tri(t, tri_bb[t]); + } + } + Vector<CoplanarCluster> newvec; + newvec.append(mergecl); + for (CoplanarCluster *cl_no_int : no_int_cls) { + newvec.append(*cl_no_int); + } + plane_cls.add_overwrite(tplane, newvec); + } + } + else { + if (dbg_level > 0) { + std::cout << "first cluster for its plane\n"; + } + plane_cls.add_new(tplane, Vector<CoplanarCluster>{CoplanarCluster(t, tri_bb[t])}); + } + } + /* Does this give deterministic order for cluster ids? I think so, since + * hash for planes is on their values, not their addresses. */ + for (auto item : plane_cls.items()) { + for (const CoplanarCluster &cl : item.value) { + if (cl.tot_tri() > 1) { + ans.add_cluster(cl); + } + } + } + + return ans; +} + +static bool face_is_degenerate(const Face *f) +{ + const Face &face = *f; + const Vert *v0 = face[0]; + const Vert *v1 = face[1]; + const Vert *v2 = face[2]; + if (v0 == v1 || v0 == v2 || v1 == v2) { + return true; + } + double3 da = v2->co - v0->co; + double3 db = v2->co - v1->co; + double3 dab = double3::cross_high_precision(da, db); + double dab_length_squared = dab.length_squared(); + double err_bound = supremum_dot_cross(dab, dab) * index_dot_cross * DBL_EPSILON; + if (dab_length_squared > err_bound) { + return false; + } + mpq3 a = v2->co_exact - v0->co_exact; + mpq3 b = v2->co_exact - v1->co_exact; + mpq3 ab = mpq3::cross(a, b); + if (ab.x == 0 && ab.y == 0 && ab.z == 0) { + return true; + } + + return false; +} + +/* Data and functions to test triangle degeneracy in parallel. */ +struct DegenData { + const IMesh &tm; +}; + +struct DegenChunkData { + bool has_degenerate_tri = false; +}; + +static void degenerate_range_func(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict tls) +{ + DegenData *data = static_cast<DegenData *>(userdata); + DegenChunkData *chunk_data = static_cast<DegenChunkData *>(tls->userdata_chunk); + const Face *f = data->tm.face(iter); + bool is_degenerate = face_is_degenerate(f); + chunk_data->has_degenerate_tri |= is_degenerate; +} + +static void degenerate_reduce(const void *__restrict UNUSED(userdata), + void *__restrict chunk_join, + void *__restrict chunk) +{ + DegenChunkData *degen_chunk_join = static_cast<DegenChunkData *>(chunk_join); + DegenChunkData *degen_chunk = static_cast<DegenChunkData *>(chunk); + degen_chunk_join->has_degenerate_tri |= degen_chunk->has_degenerate_tri; +} + +/* Does triangle #IMesh tm have any triangles with zero area? */ +static bool has_degenerate_tris(const IMesh &tm) +{ + DegenData degen_data = {tm}; + DegenChunkData degen_chunk_data; + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.userdata_chunk = °en_chunk_data; + settings.userdata_chunk_size = sizeof(degen_chunk_data); + settings.func_reduce = degenerate_reduce; + settings.min_iter_per_thread = 1000; + settings.use_threading = intersect_use_threading; + BLI_task_parallel_range(0, tm.face_size(), °en_data, degenerate_range_func, &settings); + return degen_chunk_data.has_degenerate_tri; +} + +static IMesh remove_degenerate_tris(const IMesh &tm_in) +{ + IMesh ans; + Vector<Face *> new_faces; + new_faces.reserve(tm_in.face_size()); + for (Face *f : tm_in.faces()) { + if (!face_is_degenerate(f)) { + new_faces.append(f); + } + } + ans.set_faces(new_faces); + return ans; +} + +/* This is the main routine for calculating the self_intersection of a triangle mesh. */ +IMesh trimesh_self_intersect(const IMesh &tm_in, IMeshArena *arena) +{ + return trimesh_nary_intersect( + tm_in, 1, [](int UNUSED(t)) { return 0; }, true, arena); +} + +IMesh trimesh_nary_intersect(const IMesh &tm_in, + int nshapes, + std::function<int(int)> shape_fn, + bool use_self, + IMeshArena *arena) +{ + constexpr int dbg_level = 0; + if (dbg_level > 0) { + std::cout << "\nTRIMESH_NARY_INTERSECT nshapes=" << nshapes << " use_self=" << use_self + << "\n"; + for (const Face *f : tm_in.faces()) { + BLI_assert(f->is_tri()); + UNUSED_VARS_NDEBUG(f); + } + if (dbg_level > 1) { + std::cout << "input mesh:\n" << tm_in; + for (int t : tm_in.face_index_range()) { + std::cout << "shape(" << t << ") = " << shape_fn(tm_in.face(t)->orig) << "\n"; + } + } + } +# ifdef PERFDEBUG + perfdata_init(); + double start_time = PIL_check_seconds_timer(); + std::cout << "trimesh_nary_intersect start\n"; +# endif + /* Usually can use tm_in but if it has degenerate or illegal triangles, + * then need to work on a copy of it without those triangles. */ + const IMesh *tm_clean = &tm_in; + IMesh tm_cleaned; + if (has_degenerate_tris(tm_in)) { + if (dbg_level > 0) { + std::cout << "cleaning degenerate triangles\n"; + } + tm_cleaned = remove_degenerate_tris(tm_in); + tm_clean = &tm_cleaned; + if (dbg_level > 1) { + std::cout << "cleaned input mesh:\n" << tm_cleaned; + } + } + /* Temporary, while developing: populate all plane normals exactly. */ + for (Face *f : tm_clean->faces()) { + f->populate_plane(true); + } +# ifdef PERFDEBUG + double clean_time = PIL_check_seconds_timer(); + std::cout << "cleaned, time = " << clean_time - start_time << "\n"; +# endif + Array<BoundingBox> tri_bb = calc_face_bounding_boxes(*tm_clean); +# ifdef PERFDEBUG + double bb_calc_time = PIL_check_seconds_timer(); + std::cout << "bbs calculated, time = " << bb_calc_time - clean_time << "\n"; +# endif + TriOverlaps tri_ov(*tm_clean, tri_bb, nshapes, shape_fn, use_self); +# ifdef PERFDEBUG + double overlap_time = PIL_check_seconds_timer(); + std::cout << "intersect overlaps calculated, time = " << overlap_time - bb_calc_time << "\n"; + +# endif + CoplanarClusterInfo clinfo = find_clusters(*tm_clean, tri_bb); + if (dbg_level > 1) { + std::cout << clinfo; + } +# ifdef PERFDEBUG + double find_cluster_time = PIL_check_seconds_timer(); + std::cout << "clusters found, time = " << find_cluster_time - overlap_time << "\n"; + doperfmax(0, tm_in.face_size()); + doperfmax(1, clinfo.tot_cluster()); + doperfmax(2, tri_ov.overlap().size()); +# endif + /* itt_map((a,b)) will hold the intersection value resulting from intersecting + * triangles with indices a and b, where a < b. */ + Map<std::pair<int, int>, ITT_value> itt_map; + itt_map.reserve(tri_ov.overlap().size()); + calc_overlap_itts(itt_map, *tm_clean, clinfo, tri_ov, arena); +# ifdef PERFDEBUG + double itt_time = PIL_check_seconds_timer(); + std::cout << "itts found, time = " << itt_time - find_cluster_time << "\n"; +# endif + Array<IMesh> tri_subdivided(tm_clean->face_size()); + calc_subdivided_tris(tri_subdivided, *tm_clean, itt_map, clinfo, tri_ov, arena); +# ifdef PERFDEBUG + double subdivided_tris_time = PIL_check_seconds_timer(); + std::cout << "subdivided tris found, time = " << subdivided_tris_time - itt_time << "\n"; +# endif + Array<CDT_data> cluster_subdivided(clinfo.tot_cluster()); + for (int c : clinfo.index_range()) { + cluster_subdivided[c] = calc_cluster_subdivided(clinfo, c, *tm_clean, tri_ov, itt_map, arena); + } +# ifdef PERFDEBUG + double cluster_subdivide_time = PIL_check_seconds_timer(); + std::cout << "subdivided clusters found, time = " + << cluster_subdivide_time - subdivided_tris_time << "\n"; +# endif + for (int t : tm_clean->face_index_range()) { + int c = clinfo.tri_cluster(t); + if (c != NO_INDEX) { + BLI_assert(tri_subdivided[t].face_size() == 0); + tri_subdivided[t] = extract_subdivided_tri(cluster_subdivided[c], *tm_clean, t, arena); + } + else if (tri_subdivided[t].face_size() == 0) { + tri_subdivided[t] = extract_single_tri(*tm_clean, t); + } + } +# ifdef PERFDEBUG + double extract_time = PIL_check_seconds_timer(); + std::cout << "triangles extracted, time = " << extract_time - cluster_subdivide_time << "\n"; +# endif + IMesh combined = union_tri_subdivides(tri_subdivided); + if (dbg_level > 1) { + std::cout << "TRIMESH_NARY_INTERSECT answer:\n"; + std::cout << combined; + } +# ifdef PERFDEBUG + double end_time = PIL_check_seconds_timer(); + std::cout << "triangles combined, time = " << end_time - extract_time << "\n"; + std::cout << "trimesh_nary_intersect done, total time = " << end_time - start_time << "\n"; + dump_perfdata(); +# endif + return combined; +} + +static std::ostream &operator<<(std::ostream &os, const CoplanarCluster &cl) +{ + os << "cl("; + bool first = true; + for (const int t : cl) { + if (first) { + first = false; + } + else { + os << ","; + } + os << t; + } + os << ")"; + return os; +} + +static std::ostream &operator<<(std::ostream &os, const CoplanarClusterInfo &clinfo) +{ + os << "Coplanar Cluster Info:\n"; + for (int c : clinfo.index_range()) { + os << c << ": " << clinfo.cluster(c) << "\n"; + } + return os; +} + +static std::ostream &operator<<(std::ostream &os, const ITT_value &itt) +{ + switch (itt.kind) { + case INONE: + os << "none"; + break; + case IPOINT: + os << "point " << itt.p1; + break; + case ISEGMENT: + os << "segment " << itt.p1 << " " << itt.p2; + break; + case ICOPLANAR: + os << "co-planar t" << itt.t_source; + break; + } + return os; +} + +/** + * Writing the obj_mesh has the side effect of populating verts. + */ +void write_obj_mesh(IMesh &m, const std::string &objname) +{ + /* Would like to use #BKE_tempdir_base() here, but that brings in dependence on kernel library. + * This is just for developer debugging anyway, + * and should never be called in production Blender. */ +# ifdef _WIN_32 + const char *objdir = BLI_getenv("HOME"); +# else + const char *objdir = "/tmp/"; +# endif + if (m.face_size() == 0) { + return; + } + + std::string fname = std::string(objdir) + objname + std::string(".obj"); + std::ofstream f; + f.open(fname); + if (!f) { + std::cout << "Could not open file " << fname << "\n"; + return; + } + + if (!m.has_verts()) { + m.populate_vert(); + } + for (const Vert *v : m.vertices()) { + const double3 dv = v->co; + f << "v " << dv[0] << " " << dv[1] << " " << dv[2] << "\n"; + } + int i = 0; + for (const Face *face : m.faces()) { + /* OBJ files use 1-indexing for vertices. */ + f << "f "; + for (const Vert *v : *face) { + int i = m.lookup_vert(v); + BLI_assert(i != NO_INDEX); + /* OBJ files use 1-indexing for vertices. */ + f << i + 1 << " "; + } + f << "\n"; + ++i; + } + f.close(); +} + +# ifdef PERFDEBUG +struct PerfCounts { + Vector<int> count; + Vector<const char *> count_name; + Vector<int> max; + Vector<const char *> max_name; +}; + +static PerfCounts *perfdata = nullptr; + +static void perfdata_init(void) +{ + perfdata = new PerfCounts; + + /* count 0. */ + perfdata->count.append(0); + perfdata->count_name.append("Non-cluster overlaps"); + + /* count 1. */ + perfdata->count.append(0); + perfdata->count_name.append("intersect_tri_tri calls"); + + /* count 2. */ + perfdata->count.append(0); + perfdata->count_name.append("tri tri intersects decided by filter plane tests"); + + /* count 3. */ + perfdata->count.append(0); + perfdata->count_name.append("tri tri intersects decided by exact plane tests"); + + /* count 4. */ + perfdata->count.append(0); + perfdata->count_name.append("final non-NONE intersects"); + + /* max 0. */ + perfdata->max.append(0); + perfdata->max_name.append("total faces"); + + /* max 1. */ + perfdata->max.append(0); + perfdata->max_name.append("total clusters"); + + /* max 2. */ + perfdata->max.append(0); + perfdata->max_name.append("total overlaps"); +} + +static void incperfcount(int countnum) +{ + perfdata->count[countnum]++; +} + +static void bumpperfcount(int countnum, int amt) +{ + perfdata->count[countnum] += amt; +} + +static void doperfmax(int maxnum, int val) +{ + perfdata->max[maxnum] = max_ii(perfdata->max[maxnum], val); +} + +static void dump_perfdata(void) +{ + std::cout << "\nPERFDATA\n"; + for (int i : perfdata->count.index_range()) { + std::cout << perfdata->count_name[i] << " = " << perfdata->count[i] << "\n"; + } + for (int i : perfdata->max.index_range()) { + std::cout << perfdata->max_name[i] << " = " << perfdata->max[i] << "\n"; + } + delete perfdata; +} +# endif + +}; // namespace blender::meshintersect + +#endif // WITH_GMP diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 67d41ffb779..cde4394a8c3 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -532,7 +532,7 @@ void BLI_path_rel(char *file, const char *relfile) char *ptemp; /* fix missing volume name in relative base, * can happen with old recent-files.txt files */ - get_default_root(temp); + BLI_windows_get_default_root_dir(temp); ptemp = &temp[2]; if (relfile[0] != '\\' && relfile[0] != '/') { ptemp++; @@ -1026,7 +1026,7 @@ bool BLI_path_abs(char *path, const char *basepath) */ if (!wasrelative && !BLI_path_is_abs(path)) { char *p = path; - get_default_root(tmp); + BLI_windows_get_default_root_dir(tmp); // get rid of the slashes at the beginning of the path while (ELEM(*p, '\\', '/')) { p++; @@ -1385,7 +1385,7 @@ void BLI_make_file_string(const char *relabase, char *string, const char *dir, c string[3] = '\0'; } else { /* we're out of luck here, guessing the first valid drive, usually c:\ */ - get_default_root(string); + BLI_windows_get_default_root_dir(string); } /* ignore leading slashes */ diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index df7e7107d11..333b6783087 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -36,14 +36,12 @@ # include "BLI_utildefines.h" # include "BLI_winstuff.h" -# include "../blenkernel/BKE_global.h" /* G.background, bad level include (no function calls) */ - # include "utf_winfunc.h" # include "utfconv.h" /* FILE_MAXDIR + FILE_MAXFILE */ -int BLI_getInstallationDir(char *str) +int BLI_windows_get_executable_dir(char *str) { char dir[FILE_MAXDIR]; int a; @@ -60,19 +58,19 @@ int BLI_getInstallationDir(char *str) return 1; } -static void RegisterBlendExtension_Fail(HKEY root) +static void register_blend_extension_failed(HKEY root, const bool background) { printf("failed\n"); if (root) { RegCloseKey(root); } - if (!G.background) { + if (!background) { MessageBox(0, "Could not register file extension.", "Blender error", MB_OK | MB_ICONERROR); } TerminateProcess(GetCurrentProcess(), 1); } -void RegisterBlendExtension(void) +void BLI_windows_register_blend_extension(const bool background) { LONG lresult; HKEY hkey = 0; @@ -108,7 +106,7 @@ void RegisterBlendExtension(void) usr_mode = true; lresult = RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\Classes", 0, KEY_ALL_ACCESS, &root); if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(0); + register_blend_extension_failed(0, background); } } @@ -120,7 +118,7 @@ void RegisterBlendExtension(void) RegCloseKey(hkey); } if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(root); + register_blend_extension_failed(root, background); } lresult = RegCreateKeyEx(root, @@ -138,7 +136,7 @@ void RegisterBlendExtension(void) RegCloseKey(hkey); } if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(root); + register_blend_extension_failed(root, background); } lresult = RegCreateKeyEx(root, @@ -156,7 +154,7 @@ void RegisterBlendExtension(void) RegCloseKey(hkey); } if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(root); + register_blend_extension_failed(root, background); } lresult = RegCreateKeyEx( @@ -167,10 +165,10 @@ void RegisterBlendExtension(void) RegCloseKey(hkey); } if (lresult != ERROR_SUCCESS) { - RegisterBlendExtension_Fail(root); + register_blend_extension_failed(root, background); } - BLI_getInstallationDir(InstallDir); + BLI_windows_get_executable_dir(InstallDir); GetSystemDirectory(SysDir, FILE_MAXDIR); ThumbHandlerDLL = "BlendThumb.dll"; snprintf( @@ -179,7 +177,7 @@ void RegisterBlendExtension(void) RegCloseKey(root); printf("success (%s)\n", usr_mode ? "user" : "system"); - if (!G.background) { + if (!background) { sprintf(MBox, "File extension registered for %s.", usr_mode ? "the current user. To register for all users, run as an administrator" : @@ -189,7 +187,7 @@ void RegisterBlendExtension(void) TerminateProcess(GetCurrentProcess(), 0); } -void get_default_root(char *root) +void BLI_windows_get_default_root_dir(char *root) { char str[MAX_PATH + 1]; @@ -236,7 +234,7 @@ void get_default_root(char *root) } } if (0 == rc) { - printf("ERROR in 'get_default_root': can't find a valid drive!\n"); + printf("ERROR in 'BLI_windows_get_default_root_dir': can't find a valid drive!\n"); root[0] = 'C'; root[1] = ':'; root[2] = '\\'; @@ -246,30 +244,6 @@ void get_default_root(char *root) } } -/* UNUSED */ -# if 0 -int check_file_chars(char *filename) -{ - char *p = filename; - while (*p) { - switch (*p) { - case ':': - case '?': - case '*': - case '|': - case '\\': - case '/': - case '\"': - return 0; - break; - } - - p++; - } - return 1; -} -# endif - #else /* intentionally empty for UNIX */ diff --git a/source/blender/blenlib/tests/BLI_array_test.cc b/source/blender/blenlib/tests/BLI_array_test.cc index 38ab695d238..7d967eca87e 100644 --- a/source/blender/blenlib/tests/BLI_array_test.cc +++ b/source/blender/blenlib/tests/BLI_array_test.cc @@ -1,6 +1,7 @@ /* Apache License, Version 2.0 */ #include "BLI_array.hh" +#include "BLI_exception_safety_test_utils.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" #include "testing/testing.h" @@ -188,4 +189,65 @@ TEST(array, ReverseIterator) EXPECT_EQ_ARRAY(array.data(), Span({13, 14, 15, 16}).data(), 4); } +TEST(array, SpanConstructorExceptions) +{ + std::array<ExceptionThrower, 4> values; + values[2].throw_during_copy = true; + Span<ExceptionThrower> span{values}; + EXPECT_ANY_THROW({ Array<ExceptionThrower> array(span); }); +} + +TEST(array, SizeValueConstructorExceptions) +{ + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ Array<ExceptionThrower> array(5, value); }); +} + +TEST(array, MoveConstructorExceptions) +{ + Array<ExceptionThrower, 4> array(3); + array[1].throw_during_move = true; + EXPECT_ANY_THROW({ Array<ExceptionThrower> array_copy(std::move(array)); }); +} + +TEST(array, CopyAssignmentExceptions) +{ + Array<ExceptionThrower> array(5); + array[3].throw_during_copy = true; + Array<ExceptionThrower> array_copy(10); + EXPECT_ANY_THROW({ array_copy = array; }); +} + +TEST(array, MoveAssignmentExceptions) +{ + Array<ExceptionThrower, 4> array(4); + array[2].throw_during_move = true; + Array<ExceptionThrower> array_moved(10); + EXPECT_ANY_THROW({ array_moved = std::move(array); }); +} + +TEST(array, Last) +{ + Array<int> array = {5, 7, 8, 9}; + EXPECT_EQ(array.last(), 9); + array.last() = 1; + EXPECT_EQ(array[3], 1); + EXPECT_EQ(const_cast<const Array<int> &>(array).last(), 1); +} + +TEST(array, Reinitialize) +{ + Array<std::string> array = {"hello", "world"}; + EXPECT_EQ(array.size(), 2); + EXPECT_EQ(array[1], "world"); + array.reinitialize(3); + EXPECT_EQ(array.size(), 3); + EXPECT_EQ(array[0], ""); + EXPECT_EQ(array[1], ""); + EXPECT_EQ(array[2], ""); + array.reinitialize(0); + EXPECT_EQ(array.size(), 0); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc index 752f833461d..2287389f7aa 100644 --- a/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc +++ b/source/blender/blenlib/tests/BLI_delaunay_2d_test.cc @@ -4,48 +4,30 @@ #include "MEM_guardedalloc.h" +extern "C" { #include "BLI_math.h" #include "BLI_rand.h" #include "PIL_time.h" - -#include "BLI_delaunay_2d.h" +} #include <fstream> #include <iostream> #include <sstream> +#include <type_traits> -#define DO_REGULAR_TESTS 1 +#define DO_CPP_TESTS 1 +#define DO_C_TESTS 1 #define DO_RANDOM_TESTS 0 -#define DO_FILE_TESTS 0 -static void fill_input_verts(CDT_input *r_input, float (*vcos)[2], int nverts) -{ - r_input->verts_len = nverts; - r_input->edges_len = 0; - r_input->faces_len = 0; - r_input->vert_coords = vcos; - r_input->edges = NULL; - r_input->faces = NULL; - r_input->faces_start_table = NULL; - r_input->faces_len_table = NULL; - r_input->epsilon = 1e-5f; - r_input->skip_input_modify = false; -} +#include "BLI_array.hh" +#include "BLI_double2.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mpq2.hh" +#include "BLI_vector.hh" -static void add_input_edges(CDT_input *r_input, int (*edges)[2], int nedges) -{ - r_input->edges_len = nedges; - r_input->edges = edges; -} +#include "BLI_delaunay_2d.h" -static void add_input_faces( - CDT_input *r_input, int *faces, int *faces_start_table, int *faces_len_table, int nfaces) -{ - r_input->faces_len = nfaces; - r_input->faces = faces; - r_input->faces_start_table = faces_start_table; - r_input->faces_len_table = faces_len_table; -} +namespace blender::meshintersect { /* The spec should have the form: * #verts #edges #faces @@ -53,177 +35,164 @@ static void add_input_faces( * <int> <int> [#edges lines] * <int> <int> ... <int> [#faces lines] */ -static void fill_input_from_string(CDT_input *r_input, const char *spec) +template<typename T> CDT_input<T> fill_input_from_string(const char *spec) { - std::string line; - std::vector<std::vector<int>> faces; - int i, j; - int nverts, nedges, nfaces; - float(*p)[2]; - int(*e)[2]; - int *farr; - int *flen; - int *fstart; - std::istringstream ss(spec); + std::string line; getline(ss, line); std::istringstream hdrss(line); + int nverts, nedges, nfaces; hdrss >> nverts >> nedges >> nfaces; if (nverts == 0) { - return; - } - p = (float(*)[2])MEM_malloc_arrayN(nverts, 2 * sizeof(float), __func__); - if (nedges > 0) { - e = (int(*)[2])MEM_malloc_arrayN(nedges, 2 * sizeof(int), __func__); - } - if (nfaces > 0) { - flen = (int *)MEM_malloc_arrayN(nfaces, sizeof(int), __func__); - fstart = (int *)MEM_malloc_arrayN(nfaces, sizeof(int), __func__); + return CDT_input<T>(); } - i = 0; + Array<vec2<T>> verts(nverts); + Array<std::pair<int, int>> edges(nedges); + Array<Vector<int>> faces(nfaces); + int i = 0; while (i < nverts && getline(ss, line)) { std::istringstream iss(line); - iss >> p[i][0] >> p[i][1]; + double dp0, dp1; + iss >> dp0 >> dp1; + T p0(dp0); + T p1(dp1); + verts[i] = vec2<T>(p0, p1); i++; } i = 0; while (i < nedges && getline(ss, line)) { std::istringstream ess(line); - ess >> e[i][0] >> e[i][1]; + int e0, e1; + ess >> e0 >> e1; + edges[i] = std::pair<int, int>(e0, e1); i++; } i = 0; while (i < nfaces && getline(ss, line)) { std::istringstream fss(line); int v; - faces.push_back(std::vector<int>()); while (fss >> v) { - faces[i].push_back(v); + faces[i].append(v); } i++; } - fill_input_verts(r_input, p, nverts); - if (nedges > 0) { - add_input_edges(r_input, e, nedges); + CDT_input<T> ans; + ans.vert = verts; + ans.edge = edges; + ans.face = faces; +#ifdef WITH_GMP + if (std::is_same<mpq_class, T>::value) { + ans.epsilon = T(0); } - if (nfaces > 0) { - for (i = 0; i < nfaces; i++) { - flen[i] = (int)faces[i].size(); - if (i == 0) { - fstart[i] = 0; - } - else { - fstart[i] = fstart[i - 1] + flen[i - 1]; - } - } - farr = (int *)MEM_malloc_arrayN(fstart[nfaces - 1] + flen[nfaces - 1], sizeof(int), __func__); - for (i = 0; i < nfaces; i++) { - for (j = 0; j < (int)faces[i].size(); j++) { - farr[fstart[i] + j] = faces[i][j]; + else { + ans.epsilon = T(0.00001); + } +#else + ans.epsilon = T(0.00001); +#endif + return ans; +} + +/* Find an original index in a table mapping new to original. + * Return -1 if not found. + */ +static int get_orig_index(const Array<Vector<int>> &out_to_orig, int orig_index) +{ + int n = static_cast<int>(out_to_orig.size()); + for (int i = 0; i < n; ++i) { + for (int orig : out_to_orig[i]) { + if (orig == orig_index) { + return i; } } - add_input_faces(r_input, farr, fstart, flen, nfaces); } + return -1; } -#if DO_FILE_TESTS -static void fill_input_from_file(CDT_input *in, const char *filename) +template<typename T> static double math_to_double(const T UNUSED(v)) { - std::FILE *fp = std::fopen(filename, "rb"); - if (fp) { - std::string contents; - std::fseek(fp, 0, SEEK_END); - contents.resize(std::ftell(fp)); - std::rewind(fp); - std::fread(&contents[0], 1, contents.size(), fp); - std::fclose(fp); - fill_input_from_string(in, contents.c_str()); - } - else { - printf("couldn't open file %s\n", filename); - } + BLI_assert(false); /* Need implementation for other type. */ + return 0.0; } -#endif -static void free_spec_arrays(CDT_input *in) +template<> double math_to_double<double>(const double v) { - if (in->vert_coords) { - MEM_freeN(in->vert_coords); - } - if (in->edges) { - MEM_freeN(in->edges); - } - if (in->faces_len_table) { - MEM_freeN(in->faces_len_table); - MEM_freeN(in->faces_start_table); - MEM_freeN(in->faces); - } + return v; } -/* which output vert index goes with given input vertex? -1 if not found */ -static int get_output_vert_index(const CDT_result *r, int in_index) +#ifdef WITH_GMP +template<> double math_to_double<mpq_class>(const mpq_class v) { - int i, j; + return v.get_d(); +} +#endif - for (i = 0; i < r->verts_len; i++) { - for (j = 0; j < r->verts_orig_len_table[i]; j++) { - if (r->verts_orig[r->verts_orig_start_table[i] + j] == in_index) { - return i; - } - } - } - return -1; +template<typename T> static T math_abs(const T v); + +#ifdef WITH_GMP +template<> mpq_class math_abs(const mpq_class v) +{ + return abs(v); } +#endif -/* which output edge index is for given output vert indices? */ -static int get_edge(const CDT_result *r, int out_index_1, int out_index_2) +template<> double math_abs(const double v) { - int i; + return fabs(v); +} - for (i = 0; i < r->edges_len; i++) { - if ((r->edges[i][0] == out_index_1 && r->edges[i][1] == out_index_2) || - (r->edges[i][0] == out_index_2 && r->edges[i][1] == out_index_1)) { +/* Find an output index corresponding to a given coordinate (appproximately). + * Return -1 if not found. + */ +template<typename T> int get_vertex_by_coord(const CDT_result<T> &out, double x, double y) +{ + int nv = static_cast<int>(out.vert.size()); + for (int i = 0; i < nv; ++i) { + double vx = math_to_double(out.vert[i][0]); + double vy = math_to_double(out.vert[i][1]); + if (fabs(vx - x) <= 1e-5 && fabs(vy - y) <= 1e-5) { return i; } } return -1; } -/* return true if given output edge has given input edge id in its originals list */ -static bool out_edge_has_input_id(const CDT_result *r, int out_edge_index, int in_edge_index) +/* Find an edge between two given output vertex indices. -1 if not found, */ +template<typename T> +int get_output_edge_index(const CDT_result<T> &out, int out_index_1, int out_index_2) { - if (r->edges_orig == NULL) { - return false; - } - if (out_edge_index < 0 || out_edge_index >= r->edges_len) { - return false; - } - for (int i = 0; i < r->edges_orig_len_table[out_edge_index]; i++) { - if (r->edges_orig[r->edges_orig_start_table[out_edge_index] + i] == in_edge_index) { - return true; + int ne = static_cast<int>(out.edge.size()); + for (int i = 0; i < ne; ++i) { + if ((out.edge[i].first == out_index_1 && out.edge[i].second == out_index_2) || + (out.edge[i].first == out_index_2 && out.edge[i].second == out_index_1)) { + return i; } } - return false; + return -1; } -/* which face is for given output vertex ngon? */ -static int get_face(const CDT_result *r, const int *out_indices, int nverts) +template<typename T> +bool output_edge_has_input_id(const CDT_result<T> &out, int out_edge_index, int in_edge_index) { - int f, cycle_start, k, fstart; - bool ok; + return out_edge_index < static_cast<int>(out.edge_orig.size()) && + out.edge_orig[out_edge_index].contains(in_edge_index); +} - if (r->faces_len == 0) { - return -1; - } - for (f = 0; f < r->faces_len; f++) { - if (r->faces_len_table[f] != nverts) { +/* Which out face is for a give output vertex ngon? -1 if not found. + * Allow for cyclic shifts vertices of one poly vs the other. + */ +template<typename T> int get_output_face_index(const CDT_result<T> &out, const Array<int> &poly) +{ + int nf = static_cast<int>(out.face.size()); + int npolyv = static_cast<int>(poly.size()); + for (int f = 0; f < nf; ++f) { + if (out.face[f].size() != poly.size()) { continue; } - fstart = r->faces_start_table[f]; - for (cycle_start = 0; cycle_start < nverts; cycle_start++) { - ok = true; - for (k = 0; ok && k < nverts; k++) { - if (r->faces[fstart + ((cycle_start + k) % nverts)] != out_indices[k]) { + for (int cycle_start = 0; cycle_start < npolyv; ++cycle_start) { + bool ok = true; + for (int k = 0; ok && k < npolyv; ++k) { + if (out.face[f][(cycle_start + k) % npolyv] != poly[k]) { ok = false; } } @@ -235,240 +204,286 @@ static int get_face(const CDT_result *r, const int *out_indices, int nverts) return -1; } -static int get_face_tri(const CDT_result *r, int out_index_1, int out_index_2, int out_index_3) +template<typename T> +int get_output_tri_index(const CDT_result<T> &out, + int out_index_1, + int out_index_2, + int out_index_3) { - int tri[3]; + Array<int> tri{out_index_1, out_index_2, out_index_3}; + return get_output_face_index(out, tri); +} - tri[0] = out_index_1; - tri[1] = out_index_2; - tri[2] = out_index_3; - return get_face(r, tri, 3); +template<typename T> +bool output_face_has_input_id(const CDT_result<T> &out, int out_face_index, int in_face_index) +{ + return out_face_index < static_cast<int>(out.face_orig.size()) && + out.face_orig[out_face_index].contains(in_face_index); } -/* return true if given otuput face has given input face id in its originals list */ -static bool out_face_has_input_id(const CDT_result *r, int out_face_index, int in_face_index) +/* For debugging. */ +template<typename T> std::ostream &operator<<(std::ostream &os, const CDT_result<T> &r) { - if (r->faces_orig == NULL) { - return false; + os << "\nRESULT\n"; + os << r.vert.size() << " verts, " << r.edge.size() << " edges, " << r.face.size() << " faces\n"; + os << "\nVERTS\n"; + for (int i : r.vert.index_range()) { + os << "v" << i << " = " << r.vert[i] << "\n"; + os << " orig: "; + for (int j : r.vert_orig[i].index_range()) { + os << r.vert_orig[i][j] << " "; + } + os << "\n"; } - if (out_face_index < 0 || out_face_index >= r->faces_len) { - return false; + os << "\nEDGES\n"; + for (int i : r.edge.index_range()) { + os << "e" << i << " = (" << r.edge[i].first << ", " << r.edge[i].second << ")\n"; + os << " orig: "; + for (int j : r.edge_orig[i].size()) { + os << r.edge_orig[i][j] << " "; + } + os << "\n"; } - for (int i = 0; i < r->faces_orig_len_table[out_face_index]; i++) { - if (r->faces_orig[r->faces_orig_start_table[out_face_index] + i] == in_face_index) { - return true; + os << "\nFACES\n"; + for (int i : r.face.index_range()) { + os << "f" << i << " = "; + for (int j : r.face[i].index_range()) { + os << r.face[i][j] << " "; } + os << "\n"; + os << " orig: "; + for (int j : r.face_orig[i].index_range()) { + os << r.face_orig[i][j] << " "; + } + os << "\n"; } - return false; + return os; } -#if DO_FILE_TESTS -/* for debugging */ -static void dump_result(CDT_result *r) -{ - int i, j; +static bool draw_append = false; /* Will be set to true after first call. */ - fprintf(stderr, "\nRESULT\n"); - fprintf(stderr, - "verts_len=%d edges_len=%d faces_len=%d\n", - r->verts_len, - r->edges_len, - r->faces_len); - fprintf(stderr, "\nvert coords:\n"); - for (i = 0; i < r->verts_len; i++) { - fprintf(stderr, "%d: (%f,%f)\n", i, r->vert_coords[i][0], r->vert_coords[i][1]); +template<typename T> +void graph_draw(const std::string &label, + const Array<vec2<T>> &verts, + const Array<std::pair<int, int>> &edges, + const Array<Vector<int>> &UNUSED(faces)) +{ + /* Would like to use BKE_tempdir_base() here, but that brings in dependence on kernel library. + * This is just for developer debugging anyway, and should never be called in production Blender. + */ +#ifdef WIN32 + constexpr const char *drawfile = "./cdt_test_draw.html"; +#else + constexpr const char *drawfile = "/tmp/cdt_test_draw.html"; +#endif + constexpr int max_draw_width = 1400; + constexpr int max_draw_height = 1000; + constexpr int thin_line = 1; + constexpr int vert_radius = 3; + constexpr bool draw_vert_labels = true; + constexpr bool draw_edge_labels = false; + + if (verts.size() == 0) { + return; } - fprintf(stderr, "vert orig:\n"); - for (i = 0; i < r->verts_len; i++) { - fprintf(stderr, "%d:", i); - for (j = 0; j < r->verts_orig_len_table[i]; j++) { - fprintf(stderr, " %d", r->verts_orig[r->verts_orig_start_table[i] + j]); + vec2<double> vmin(1e10, 1e10); + vec2<double> vmax(-1e10, -1e10); + for (const vec2<T> &v : verts) { + for (int i = 0; i < 2; ++i) { + double dvi = math_to_double(v[i]); + if (dvi < vmin[i]) { + vmin[i] = dvi; + } + if (dvi > vmax[i]) { + vmax[i] = dvi; + } } - fprintf(stderr, "\n"); } - fprintf(stderr, "\nedges:\n"); - for (i = 0; i < r->edges_len; i++) { - fprintf(stderr, "%d: (%d,%d)\n", i, r->edges[i][0], r->edges[i][1]); + double draw_margin = ((vmax.x - vmin.x) + (vmax.y - vmin.y)) * 0.05; + double minx = vmin.x - draw_margin; + double maxx = vmax.x + draw_margin; + double miny = vmin.y - draw_margin; + double maxy = vmax.y + draw_margin; + + double width = maxx - minx; + double height = maxy - miny; + double aspect = height / width; + int view_width = max_draw_width; + int view_height = static_cast<int>(view_width * aspect); + if (view_height > max_draw_height) { + view_height = max_draw_height; + view_width = static_cast<int>(view_height / aspect); } - if (r->edges_orig) { - fprintf(stderr, "edge orig:\n"); - for (i = 0; i < r->edges_len; i++) { - fprintf(stderr, "%d:", i); - for (j = 0; j < r->edges_orig_len_table[i]; j++) { - fprintf(stderr, " %d", r->edges_orig[r->edges_orig_start_table[i] + j]); - } - fprintf(stderr, "\n"); - } + double scale = view_width / width; + +#define SX(x) ((math_to_double(x) - minx) * scale) +#define SY(y) ((maxy - math_to_double(y)) * scale) + + std::ofstream f; + if (draw_append) { + f.open(drawfile, std::ios_base::app); } - fprintf(stderr, "\nfaces:\n"); - for (i = 0; i < r->faces_len; i++) { - fprintf(stderr, "%d: ", i); - for (j = 0; j < r->faces_len_table[i]; j++) { - fprintf(stderr, " %d", r->faces[r->faces_start_table[i] + j]); + else { + f.open(drawfile); + } + if (!f) { + std::cout << "Could not open file " << drawfile << "\n"; + return; + } + + f << "<div>" << label << "</div>\n<div>\n" + << "<svg version=\"1.1\" " + "xmlns=\"http://www.w3.org/2000/svg\" " + "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " + "xml:space=\"preserve\"\n" + << "width=\"" << view_width << "\" height=\"" << view_height << "\">n"; + + for (const std::pair<int, int> &e : edges) { + const vec2<T> &uco = verts[e.first]; + const vec2<T> &vco = verts[e.second]; + int strokew = thin_line; + f << "<line fill=\"none\" stroke=\"black\" stroke-width=\"" << strokew << "\" x1=\"" + << SX(uco[0]) << "\" y1=\"" << SY(uco[1]) << "\" x2=\"" << SX(vco[0]) << "\" y2=\"" + << SY(vco[1]) << "\">\n"; + f << " <title>[" << e.first << "][" << e.second << "]</title>\n"; + f << "</line>\n"; + if (draw_edge_labels) { + f << "<text x=\"" << SX(0.5 * (uco[0] + vco[0])) << "\" y=\"" << SY(0.5 * (uco[1] + vco[1])) + << "\" font-size=\"small\">"; + f << "[" << e.first << "][" << e.second << "]</text>\n"; } - fprintf(stderr, "\n"); } - if (r->faces_orig) { - fprintf(stderr, "face orig:\n"); - for (i = 0; i < r->faces_len; i++) { - fprintf(stderr, "%d:", i); - for (j = 0; j < r->faces_orig_len_table[i]; j++) { - fprintf(stderr, " %d", r->faces_orig[r->faces_orig_start_table[i] + j]); - } - fprintf(stderr, "\n"); + + int i = 0; + for (const vec2<T> &vco : verts) { + f << "<circle fill=\"black\" cx=\"" << SX(vco[0]) << "\" cy=\"" << SY(vco[1]) << "\" r=\"" + << vert_radius << "\">\n"; + f << " <title>[" << i << "]" << vco << "</title>\n"; + f << "</circle>\n"; + if (draw_vert_labels) { + f << "<text x=\"" << SX(vco[0]) + vert_radius << "\" y=\"" << SY(vco[1]) - vert_radius + << "\" font-size=\"small\">[" << i << "]</text>\n"; } + ++i; } + + draw_append = true; +#undef SX +#undef SY +} + +/* Should tests draw their output to an html file? */ +constexpr bool DO_DRAW = false; + +template<typename T> void expect_coord_near(const vec2<T> &testco, const vec2<T> &refco); + +#ifdef WITH_GMP +template<> +void expect_coord_near<mpq_class>(const vec2<mpq_class> &testco, const vec2<mpq_class> &refco) +{ + EXPECT_EQ(testco[0], refco[0]); + EXPECT_EQ(testco[0], refco[0]); } #endif -#if DO_REGULAR_TESTS -TEST(delaunay, Empty) +template<> void expect_coord_near<double>(const vec2<double> &testco, const vec2<double> &refco) { - CDT_input in; - CDT_result *out; + EXPECT_NEAR(testco[0], refco[0], 1e-5); + EXPECT_NEAR(testco[1], refco[1], 1e-5); +} + +#if DO_CPP_TESTS - fill_input_verts(&in, NULL, 0); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_NE((CDT_result *)NULL, out); - EXPECT_EQ(out->verts_len, 0); - EXPECT_EQ(out->edges_len, 0); - EXPECT_EQ(out->faces_len, 0); - BLI_delaunay_2d_cdt_free(out); +template<typename T> void empty_test() +{ + CDT_input<T> in; + + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(0, out.vert.size()); + EXPECT_EQ(0, out.edge.size()); + EXPECT_EQ(0, out.face.size()); + EXPECT_EQ(0, out.vert_orig.size()); + EXPECT_EQ(0, out.edge_orig.size()); + EXPECT_EQ(0, out.face_orig.size()); } -TEST(delaunay, OnePt) +template<typename T> void onept_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(1 0 0 0.0 0.0 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 1); - EXPECT_EQ(out->edges_len, 0); - EXPECT_EQ(out->faces_len, 0); - if (out->verts_len >= 1) { - EXPECT_EQ(out->vert_coords[0][0], 0.0f); - EXPECT_EQ(out->vert_coords[0][1], 0.0f); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 1); + EXPECT_EQ(out.edge.size(), 0); + EXPECT_EQ(out.face.size(), 0); + if (out.vert.size() >= 1) { + expect_coord_near<T>(out.vert[0], vec2<T>(0, 0)); } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); } -TEST(delaunay, TwoPt) +template<typename T> void twopt_test() { - CDT_input in; - CDT_result *out; - int v0_out, v1_out, e0_out; const char *spec = R"(2 0 0 0.0 -0.75 0.0 0.75 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 2); - EXPECT_EQ(out->edges_len, 1); - EXPECT_EQ(out->faces_len, 0); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 2); + EXPECT_EQ(out.edge.size(), 1); + EXPECT_EQ(out.face.size(), 0); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); EXPECT_NE(v0_out, -1); EXPECT_NE(v1_out, -1); EXPECT_NE(v0_out, v1_out); - if (out->verts_len >= 2) { - EXPECT_NEAR(out->vert_coords[v0_out][0], 0.0, in.epsilon); - EXPECT_NEAR(out->vert_coords[v0_out][1], -0.75, in.epsilon); - EXPECT_NEAR(out->vert_coords[v1_out][0], 0.0, in.epsilon); - EXPECT_NEAR(out->vert_coords[v1_out][1], 0.75, in.epsilon); + if (out.vert.size() >= 1) { + expect_coord_near<T>(out.vert[v0_out], vec2<T>(0.0, -0.75)); + expect_coord_near<T>(out.vert[v1_out], vec2<T>(0.0, 0.75)); } - e0_out = get_edge(out, v0_out, v1_out); + int e0_out = get_output_edge_index(out, v0_out, v1_out); EXPECT_EQ(e0_out, 0); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("TwoPt", out.vert, out.edge, out.face); + } } -TEST(delaunay, ThreePt) +template<typename T> void threept_test() { - CDT_input in; - CDT_result *out; - int v0_out, v1_out, v2_out; - int e0_out, e1_out, e2_out; - int f0_out; const char *spec = R"(3 0 0 -0.1 -0.75 0.1 0.75 0.5 0.5 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 3); - EXPECT_EQ(out->edges_len, 3); - EXPECT_EQ(out->faces_len, 1); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); - v2_out = get_output_vert_index(out, 2); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 3); + EXPECT_EQ(out.edge.size(), 3); + EXPECT_EQ(out.face.size(), 1); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); + int v2_out = get_orig_index(out.vert_orig, 2); EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1); EXPECT_TRUE(v0_out != v1_out && v0_out != v2_out && v1_out != v2_out); - e0_out = get_edge(out, v0_out, v1_out); - e1_out = get_edge(out, v1_out, v2_out); - e2_out = get_edge(out, v2_out, v0_out); + int e0_out = get_output_edge_index(out, v0_out, v1_out); + int e1_out = get_output_edge_index(out, v1_out, v2_out); + int e2_out = get_output_edge_index(out, v2_out, v0_out); EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1); EXPECT_TRUE(e0_out != e1_out && e0_out != e2_out && e1_out != e2_out); - f0_out = get_face_tri(out, v0_out, v2_out, v1_out); + int f0_out = get_output_tri_index(out, v0_out, v2_out, v1_out); EXPECT_EQ(f0_out, 0); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} - -TEST(delaunay, ThreePtsMerge) -{ - CDT_input in; - CDT_result *out; - int v0_out, v1_out, v2_out; - const char *spec = R"(3 0 0 - -0.05 -0.05 - 0.05 -0.05 - 0.0 0.03660254 - )"; - - /* First with epsilon such that points are within that distance of each other */ - fill_input_from_string(&in, spec); - in.epsilon = 0.21f; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 1); - EXPECT_EQ(out->edges_len, 0); - EXPECT_EQ(out->faces_len, 0); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); - v2_out = get_output_vert_index(out, 2); - EXPECT_EQ(v0_out, 0); - EXPECT_EQ(v1_out, 0); - EXPECT_EQ(v2_out, 0); - BLI_delaunay_2d_cdt_free(out); - /* Now with epsilon such that points are farther away than that. - * Note that the points won't merge with each other if distance is - * less than .01, but that they may merge with points on the Delaunay - * triangulation lines, so make epsilon even smaller to avoid that for - * this test. - */ - in.epsilon = 0.05f; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 3); - EXPECT_EQ(out->edges_len, 3); - EXPECT_EQ(out->faces_len, 1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("ThreePt", out.vert, out.edge, out.face); + } } -TEST(delaunay, MixedPts) +template<typename T> void mixedpts_test() { - CDT_input in; - CDT_result *out; - int v0_out, v1_out, v2_out, v3_out; - int e0_out, e1_out, e2_out; + /* Edges form a chain of length 3. */ const char *spec = R"(4 3 0 0.0 0.0 -0.5 -0.5 @@ -479,135 +494,129 @@ TEST(delaunay, MixedPts) 2 3 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 6); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); - v2_out = get_output_vert_index(out, 2); - v3_out = get_output_vert_index(out, 3); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 6); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); + int v2_out = get_orig_index(out.vert_orig, 2); + int v3_out = get_orig_index(out.vert_orig, 3); EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1); - e0_out = get_edge(out, v0_out, v1_out); - e1_out = get_edge(out, v1_out, v2_out); - e2_out = get_edge(out, v2_out, v3_out); + int e0_out = get_output_edge_index(out, v0_out, v1_out); + int e1_out = get_output_edge_index(out, v1_out, v2_out); + int e2_out = get_output_edge_index(out, v2_out, v3_out); EXPECT_TRUE(e0_out != -1 && e1_out != -1 && e2_out != -1); - EXPECT_TRUE(out_edge_has_input_id(out, e0_out, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1_out, 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e2_out, 2)); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + EXPECT_TRUE(output_edge_has_input_id(out, e0_out, 0)); + EXPECT_TRUE(output_edge_has_input_id(out, e1_out, 1)); + EXPECT_TRUE(output_edge_has_input_id(out, e2_out, 2)); + if (DO_DRAW) { + graph_draw<T>("MixedPts", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad0) +template<typename T> void quad0_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 0.0 1.0 1.0 0.0 2.0 0.1 2.25 0.5 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 1, 3); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 1, 3); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad0", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad1) +template<typename T> void quad1_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 0.0 0.0 0.9 -1.0 2.0 0.0 0.9 3.0 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 0, 2); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 0, 2); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad1", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad2) +template<typename T> void quad2_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 0.5 0.0 0.15 0.2 0.3 0.4 .45 0.35 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 1, 3); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 1, 3); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad2", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad3) +template<typename T> void quad3_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 0.5 0.0 0.0 0.0 0.3 0.4 .45 0.35 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 0, 2); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 0, 2); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad3", out.vert, out.edge, out.face); + } } -TEST(delaunay, Quad4) +template<typename T> void quad4_test() { - CDT_input in; - CDT_result *out; - int e_diag_out; const char *spec = R"(4 0 0 1.0 1.0 0.0 0.0 1.0 -3.0 0.0 1.0 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - e_diag_out = get_edge(out, 0, 1); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + int e_diag_out = get_output_edge_index(out, 0, 1); EXPECT_NE(e_diag_out, -1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + if (DO_DRAW) { + graph_draw<T>("Quad4", out.vert, out.edge, out.face); + } } -TEST(delaunay, LineInSquare) +template<typename T> void lineinsquare_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(6 1 1 -0.5 -0.5 0.5 -0.5 @@ -618,20 +627,24 @@ TEST(delaunay, LineInSquare) 4 5 0 1 3 2 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->faces_len, 1); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 6); + EXPECT_EQ(out.face.size(), 6); + if (DO_DRAW) { + graph_draw<T>("LineInSquare - full", out.vert, out.edge, out.face); + } + CDT_result<T> out2 = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out2.vert.size(), 6); + EXPECT_EQ(out2.face.size(), 1); + if (DO_DRAW) { + graph_draw<T>("LineInSquare - constraints", out2.vert, out2.edge, out2.face); + } } -TEST(delaunay, CrossSegs) +template<typename T> void crosssegs_test() { - CDT_input in; - CDT_result *out; - int v0_out, v1_out, v2_out, v3_out, v_intersect; - int i; const char *spec = R"(4 2 0 -0.5 0.0 0.5 0.0 @@ -641,36 +654,37 @@ TEST(delaunay, CrossSegs) 2 3 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 5); - EXPECT_EQ(out->edges_len, 8); - EXPECT_EQ(out->faces_len, 4); - v0_out = get_output_vert_index(out, 0); - v1_out = get_output_vert_index(out, 1); - v2_out = get_output_vert_index(out, 2); - v3_out = get_output_vert_index(out, 3); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 5); + EXPECT_EQ(out.edge.size(), 8); + EXPECT_EQ(out.face.size(), 4); + int v0_out = get_orig_index(out.vert_orig, 0); + int v1_out = get_orig_index(out.vert_orig, 1); + int v2_out = get_orig_index(out.vert_orig, 2); + int v3_out = get_orig_index(out.vert_orig, 3); EXPECT_TRUE(v0_out != -1 && v1_out != -1 && v2_out != -1 && v3_out != -1); - v_intersect = -1; - for (i = 0; i < out->verts_len; i++) { - if (i != v0_out && i != v1_out && i != v2_out && i != v3_out) { - EXPECT_EQ(v_intersect, -1); - v_intersect = i; + if (out.vert.size() == 5) { + int v_intersect = -1; + for (int i = 0; i < 5; i++) { + if (i != v0_out && i != v1_out && i != v2_out && i != v3_out) { + EXPECT_EQ(v_intersect, -1); + v_intersect = i; + } + } + EXPECT_NE(v_intersect, -1); + if (v_intersect != -1) { + expect_coord_near<T>(out.vert[v_intersect], vec2<T>(0, 0)); } } - EXPECT_NE(v_intersect, -1); - if (v_intersect != -1) { - EXPECT_NEAR(out->vert_coords[v_intersect][0], 0.0f, in.epsilon); - EXPECT_NEAR(out->vert_coords[v_intersect][1], 0.0f, in.epsilon); + if (DO_DRAW) { + graph_draw<T>("CrossSegs", out.vert, out.edge, out.face); } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); } -TEST(delaunay, DiamondCross) +template<typename T> void diamondcross_test() { - CDT_input in; - CDT_result *out; + /* Diamond with constraint edge from top to bottom. Some dup verts. */ const char *spec = R"(7 5 0 0.0 0.0 1.0 3.0 @@ -686,24 +700,18 @@ TEST(delaunay, DiamondCross) 5 6 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - EXPECT_EQ(out->faces_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 4); + EXPECT_EQ(out.edge.size(), 5); + EXPECT_EQ(out.face.size(), 2); + if (DO_DRAW) { + graph_draw<T>("DiamondCross", out.vert, out.edge, out.face); + } } -TEST(delaunay, TwoDiamondsCrossed) +template<typename T> void twodiamondscross_test() { - CDT_input in; - CDT_result *out; - /* Input has some repetition of vertices, on purpose */ - int e[][2] = {{0, 1}, {1, 2}, {2, 3}, {3, 4}, {5, 6}, {6, 7}, {7, 8}, {8, 9}, {10, 11}}; - int v_out[12]; - int e_out[9], e_cross_1, e_cross_2, e_cross_3; - int i; const char *spec = R"(12 9 0 0.0 0.0 1.0 2.0 @@ -728,40 +736,43 @@ TEST(delaunay, TwoDiamondsCrossed) 10 11 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 8); - EXPECT_EQ(out->edges_len, 15); - EXPECT_EQ(out->faces_len, 8); - for (i = 0; i < 12; i++) { - v_out[i] = get_output_vert_index(out, i); - EXPECT_NE(v_out[i], -1); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 8); + EXPECT_EQ(out.edge.size(), 15); + EXPECT_EQ(out.face.size(), 8); + if (out.vert.size() == 8 && out.edge.size() == 15 && out.face.size() == 8) { + int v_out[12]; + for (int i = 0; i < 12; ++i) { + v_out[i] = get_orig_index(out.vert_orig, i); + EXPECT_NE(v_out[i], -1); + } + EXPECT_EQ(v_out[0], v_out[4]); + EXPECT_EQ(v_out[0], v_out[10]); + EXPECT_EQ(v_out[5], v_out[9]); + EXPECT_EQ(v_out[7], v_out[11]); + int e_out[9]; + for (int i = 0; i < 8; ++i) { + e_out[i] = get_output_edge_index(out, v_out[in.edge[i].first], v_out[in.edge[i].second]); + EXPECT_NE(e_out[i], -1); + } + /* there won't be a single edge for the input cross edge, but rather 3 */ + EXPECT_EQ(get_output_edge_index(out, v_out[10], v_out[11]), -1); + int e_cross_1 = get_output_edge_index(out, v_out[0], v_out[2]); + int e_cross_2 = get_output_edge_index(out, v_out[2], v_out[5]); + int e_cross_3 = get_output_edge_index(out, v_out[5], v_out[7]); + EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1); + EXPECT_TRUE(output_edge_has_input_id(out, e_cross_1, 8)); + EXPECT_TRUE(output_edge_has_input_id(out, e_cross_2, 8)); + EXPECT_TRUE(output_edge_has_input_id(out, e_cross_3, 8)); } - EXPECT_EQ(v_out[0], v_out[4]); - EXPECT_EQ(v_out[0], v_out[10]); - EXPECT_EQ(v_out[5], v_out[9]); - EXPECT_EQ(v_out[7], v_out[11]); - for (i = 0; i < 8; i++) { - e_out[i] = get_edge(out, v_out[e[i][0]], v_out[e[i][1]]); - EXPECT_NE(e_out[i], -1); + if (DO_DRAW) { + graph_draw<T>("TwoDiamondsCross", out.vert, out.edge, out.face); } - /* there won't be a single edge for the input cross edge, but rather 3 */ - EXPECT_EQ(get_edge(out, v_out[10], v_out[11]), -1); - e_cross_1 = get_edge(out, v_out[0], v_out[2]); - e_cross_2 = get_edge(out, v_out[2], v_out[5]); - e_cross_3 = get_edge(out, v_out[5], v_out[7]); - EXPECT_TRUE(e_cross_1 != -1 && e_cross_2 != -1 && e_cross_3 != -1); - EXPECT_TRUE(out_edge_has_input_id(out, e_cross_1, 8)); - EXPECT_TRUE(out_edge_has_input_id(out, e_cross_2, 8)); - EXPECT_TRUE(out_edge_has_input_id(out, e_cross_3, 8)); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} - -TEST(delaunay, ManyCross) -{ - CDT_input in; - CDT_result *out; +} + +template<typename T> void manycross_test() +{ /* Input has some repetition of vertices, on purpose */ const char *spec = R"(27 21 0 0.0 0.0 @@ -814,21 +825,18 @@ TEST(delaunay, ManyCross) 25 26 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 19); - EXPECT_EQ(out->edges_len, 46); - EXPECT_EQ(out->faces_len, 28); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 19); + EXPECT_EQ(out.edge.size(), 46); + EXPECT_EQ(out.face.size(), 28); + if (DO_DRAW) { + graph_draw<T>("ManyCross", out.vert, out.edge, out.face); + } } -TEST(delaunay, TwoFace) +template<typename T> void twoface_test() { - CDT_input in; - CDT_result *out; - int v_out[6], f0_out, f1_out, e0_out, e1_out, e2_out; - int i; const char *spec = R"(6 0 2 0.0 0.0 1.0 0.0 @@ -840,40 +848,116 @@ TEST(delaunay, TwoFace) 3 4 5 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->edges_len, 9); - EXPECT_EQ(out->faces_len, 4); - for (i = 0; i < 6; i++) { - v_out[i] = get_output_vert_index(out, i); - EXPECT_NE(v_out[i], -1); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 6); + EXPECT_EQ(out.edge.size(), 9); + EXPECT_EQ(out.face.size(), 4); + if (out.vert.size() == 6 && out.edge.size() == 9 && out.face.size() == 4) { + int v_out[6]; + for (int i = 0; i < 6; i++) { + v_out[i] = get_orig_index(out.vert_orig, i); + EXPECT_NE(v_out[i], -1); + } + int f0_out = get_output_tri_index(out, v_out[0], v_out[1], v_out[2]); + int f1_out = get_output_tri_index(out, v_out[3], v_out[4], v_out[5]); + EXPECT_NE(f0_out, -1); + EXPECT_NE(f1_out, -1); + int e0_out = get_output_edge_index(out, v_out[0], v_out[1]); + int e1_out = get_output_edge_index(out, v_out[1], v_out[2]); + int e2_out = get_output_edge_index(out, v_out[2], v_out[0]); + EXPECT_NE(e0_out, -1); + EXPECT_NE(e1_out, -1); + EXPECT_NE(e2_out, -1); + EXPECT_TRUE(output_edge_has_input_id(out, e0_out, out.face_edge_offset + 0)); + EXPECT_TRUE(output_edge_has_input_id(out, e1_out, out.face_edge_offset + 1)); + EXPECT_TRUE(output_edge_has_input_id(out, e2_out, out.face_edge_offset + 2)); + EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1)); + } + if (DO_DRAW) { + graph_draw<T>("TwoFace", out.vert, out.edge, out.face); } - f0_out = get_face(out, &v_out[0], 3); - f1_out = get_face(out, &v_out[3], 3); - EXPECT_NE(f0_out, -1); - EXPECT_NE(f1_out, -1); - e0_out = get_edge(out, v_out[0], v_out[1]); - e1_out = get_edge(out, v_out[1], v_out[2]); - e2_out = get_edge(out, v_out[2], v_out[0]); - EXPECT_NE(e0_out, -1); - EXPECT_NE(e1_out, -1); - EXPECT_NE(e2_out, -1); - EXPECT_TRUE(out_edge_has_input_id(out, e0_out, out->face_edge_offset + 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1_out, out->face_edge_offset + 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e2_out, out->face_edge_offset + 2)); - EXPECT_TRUE(out_face_has_input_id(out, f0_out, 0)); - EXPECT_TRUE(out_face_has_input_id(out, f1_out, 1)); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} - -TEST(delaunay, OverlapFaces) -{ - CDT_input in; - CDT_result *out; - int v_out[12], v_int1, v_int2, f0_out, f1_out, f2_out; - int i; +} + +template<typename T> void twoface2_test() +{ + const char *spec = R"(6 0 2 + 0.0 0.0 + 4.0 4.0 + -4.0 2.0 + 3.0 0.0 + 3.0 6.0 + -1.0 2.0 + 0 1 2 + 3 4 5 + )"; + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_INSIDE); + EXPECT_EQ(out.vert.size(), 10); + EXPECT_EQ(out.edge.size(), 18); + EXPECT_EQ(out.face.size(), 9); + if (out.vert.size() == 10 && out.edge.size() == 18 && out.face.size() == 9) { + /* Input verts have no dups, so expect output ones match input ones. */ + for (int i = 0; i < 6; i++) { + EXPECT_EQ(get_orig_index(out.vert_orig, i), i); + } + int v6 = get_vertex_by_coord(out, 3.0, 3.0); + EXPECT_NE(v6, -1); + int v7 = get_vertex_by_coord(out, 3.0, 3.75); + EXPECT_NE(v7, -1); + int v8 = get_vertex_by_coord(out, 0.0, 3.0); + EXPECT_NE(v8, -1); + int v9 = get_vertex_by_coord(out, 1.0, 1.0); + EXPECT_NE(v9, -1); + /* f0 to f3 should be triangles part of input face 0, not part of input face 1. */ + int f0 = get_output_tri_index(out, 0, 9, 5); + EXPECT_NE(f0, -1); + EXPECT_TRUE(output_face_has_input_id(out, f0, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f0, 1)); + int f1 = get_output_tri_index(out, 0, 5, 2); + EXPECT_NE(f1, -1); + EXPECT_TRUE(output_face_has_input_id(out, f1, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f1, 1)); + int f2 = get_output_tri_index(out, 2, 5, 8); + EXPECT_NE(f2, -1); + EXPECT_TRUE(output_face_has_input_id(out, f2, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f2, 1)); + int f3 = get_output_tri_index(out, 6, 1, 7); + EXPECT_NE(f3, -1); + EXPECT_TRUE(output_face_has_input_id(out, f3, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f3, 1)); + /* f4 and f5 should be triangles part of input face 1, not part of input face 0. */ + int f4 = get_output_tri_index(out, 8, 7, 4); + EXPECT_NE(f4, -1); + EXPECT_FALSE(output_face_has_input_id(out, f4, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f4, 1)); + int f5 = get_output_tri_index(out, 3, 6, 9); + EXPECT_NE(f5, -1); + EXPECT_FALSE(output_face_has_input_id(out, f5, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f5, 1)); + /* f6 to f8 should be triangles part of both input faces. */ + int f6 = get_output_tri_index(out, 5, 9, 6); + EXPECT_NE(f6, -1); + EXPECT_TRUE(output_face_has_input_id(out, f6, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f6, 1)); + int f7 = get_output_tri_index(out, 5, 6, 7); + EXPECT_NE(f7, -1); + EXPECT_TRUE(output_face_has_input_id(out, f7, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f7, 1)); + int f8 = get_output_tri_index(out, 5, 7, 8); + EXPECT_NE(f8, -1); + EXPECT_TRUE(output_face_has_input_id(out, f8, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f8, 1)); + } + if (DO_DRAW) { + graph_draw<T>("TwoFace2", out.vert, out.edge, out.face); + } +} + +template<typename T> void overlapfaces_test() +{ const char *spec = R"(12 0 3 0.0 0.0 1.0 0.0 @@ -892,64 +976,69 @@ TEST(delaunay, OverlapFaces) 8 9 10 11 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - EXPECT_EQ(out->verts_len, 14); - EXPECT_EQ(out->edges_len, 33); - EXPECT_EQ(out->faces_len, 20); - for (i = 0; i < 12; i++) { - v_out[i] = get_output_vert_index(out, i); - EXPECT_NE(v_out[i], -1); - } - v_int1 = 12; - v_int2 = 13; - if (out->verts_len > 13) { - if (fabsf(out->vert_coords[v_int1][0] - 1.0f) > in.epsilon) { + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_FULL); + EXPECT_EQ(out.vert.size(), 14); + EXPECT_EQ(out.edge.size(), 33); + EXPECT_EQ(out.face.size(), 20); + if (out.vert.size() == 14 && out.edge.size() == 33 && out.face.size() == 20) { + int v_out[12]; + for (int i = 0; i < 12; i++) { + v_out[i] = get_orig_index(out.vert_orig, i); + EXPECT_NE(v_out[i], -1); + } + int v_int1 = 12; + int v_int2 = 13; + T x = out.vert[v_int1][0] - T(1); + if (math_abs(x) > in.epsilon) { v_int1 = 13; v_int2 = 12; } - EXPECT_NEAR(out->vert_coords[v_int1][0], 1.0, in.epsilon); - EXPECT_NEAR(out->vert_coords[v_int1][1], 0.5, in.epsilon); - EXPECT_NEAR(out->vert_coords[v_int2][0], 0.5, in.epsilon); - EXPECT_NEAR(out->vert_coords[v_int2][1], 1.0, in.epsilon); - EXPECT_EQ(out->verts_orig_len_table[v_int1], 0); - EXPECT_EQ(out->verts_orig_len_table[v_int2], 0); + expect_coord_near<T>(out.vert[v_int1], vec2<T>(1, 0.5)); + expect_coord_near<T>(out.vert[v_int2], vec2<T>(0.5, 1)); + EXPECT_EQ(out.vert_orig[v_int1].size(), 0); + EXPECT_EQ(out.vert_orig[v_int2].size(), 0); + int f0_out = get_output_tri_index(out, v_out[1], v_int1, v_out[4]); + EXPECT_NE(f0_out, -1); + EXPECT_TRUE(output_face_has_input_id(out, f0_out, 0)); + int f1_out = get_output_tri_index(out, v_out[4], v_int1, v_out[2]); + EXPECT_NE(f1_out, -1); + EXPECT_TRUE(output_face_has_input_id(out, f1_out, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f1_out, 1)); + int f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[10]); + if (f2_out == -1) { + f2_out = get_output_tri_index(out, v_out[8], v_out[9], v_out[11]); + } + EXPECT_NE(f2_out, -1); + EXPECT_TRUE(output_face_has_input_id(out, f2_out, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f2_out, 2)); } - f0_out = get_face_tri(out, v_out[1], v_int1, v_out[4]); - EXPECT_NE(f0_out, -1); - EXPECT_TRUE(out_face_has_input_id(out, f0_out, 0)); - f1_out = get_face_tri(out, v_out[4], v_int1, v_out[2]); - EXPECT_NE(f1_out, -1); - EXPECT_TRUE(out_face_has_input_id(out, f1_out, 0)); - EXPECT_TRUE(out_face_has_input_id(out, f1_out, 1)); - f2_out = get_face_tri(out, v_out[8], v_out[9], v_out[10]); - if (f2_out == -1) { - f2_out = get_face_tri(out, v_out[8], v_out[9], v_out[11]); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - full", out.vert, out.edge, out.face); } - EXPECT_NE(f2_out, -1); - EXPECT_TRUE(out_face_has_input_id(out, f2_out, 0)); - EXPECT_TRUE(out_face_has_input_id(out, f2_out, 2)); - BLI_delaunay_2d_cdt_free(out); - /* Different output types */ - out = BLI_delaunay_2d_cdt_calc(&in, CDT_INSIDE); - EXPECT_EQ(out->faces_len, 18); - BLI_delaunay_2d_cdt_free(out); + /* Different output types. */ + CDT_result<T> out2 = delaunay_2d_calc(in, CDT_INSIDE); + EXPECT_EQ(out2.face.size(), 18); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - inside", out2.vert, out2.edge, out2.face); + } - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->faces_len, 4); - BLI_delaunay_2d_cdt_free(out); + CDT_result<T> out3 = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out3.face.size(), 4); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - constraints", out3.vert, out3.edge, out3.face); + } - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out->faces_len, 5); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_result<T> out4 = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out4.face.size(), 5); + if (DO_DRAW) { + graph_draw<T>("OverlapFaces - valid bmesh", out4.vert, out4.edge, out4.face); + } } -TEST(delaunay, TwoSquaresOverlap) +template<typename T> void twosquaresoverlap_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(8 0 2 1.0 -1.0 -1.0 -1.0 @@ -963,22 +1052,18 @@ TEST(delaunay, TwoSquaresOverlap) 3 2 1 0 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out->verts_len, 10); - EXPECT_EQ(out->edges_len, 12); - EXPECT_EQ(out->faces_len, 3); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out.vert.size(), 10); + EXPECT_EQ(out.edge.size(), 12); + EXPECT_EQ(out.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("TwoSquaresOverlap", out.vert, out.edge, out.face); + } } -TEST(delaunay, TwoFaceEdgeOverlap) +template<typename T> void twofaceedgeoverlap_test() { - CDT_input in; - CDT_result *out; - int i, v_out[6], v_int; - int e01, e1i, ei2, e20, e24, e4i, ei0; - int f02i, f24i, f10i; const char *spec = R"(6 0 2 5.657 0.0 -1.414 -5.831 @@ -990,56 +1075,57 @@ TEST(delaunay, TwoFaceEdgeOverlap) 5 4 3 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 5); - EXPECT_EQ(out->edges_len, 7); - EXPECT_EQ(out->faces_len, 3); - if (out->verts_len == 5 && out->edges_len == 7 && out->faces_len == 3) { - v_int = 4; - for (i = 0; i < 6; i++) { - v_out[i] = get_output_vert_index(out, i); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out.vert.size(), 5); + EXPECT_EQ(out.edge.size(), 7); + EXPECT_EQ(out.face.size(), 3); + if (out.vert.size() == 5 && out.edge.size() == 7 && out.face.size() == 3) { + int v_int = 4; + int v_out[6]; + for (int i = 0; i < 6; i++) { + v_out[i] = get_orig_index(out.vert_orig, i); EXPECT_NE(v_out[i], -1); EXPECT_NE(v_out[i], v_int); } EXPECT_EQ(v_out[0], v_out[3]); EXPECT_EQ(v_out[2], v_out[5]); - e01 = get_edge(out, v_out[0], v_out[1]); - EXPECT_TRUE(out_edge_has_input_id(out, e01, 1)); - e1i = get_edge(out, v_out[1], v_int); - EXPECT_TRUE(out_edge_has_input_id(out, e1i, 0)); - ei2 = get_edge(out, v_int, v_out[2]); - EXPECT_TRUE(out_edge_has_input_id(out, ei2, 0)); - e20 = get_edge(out, v_out[2], v_out[0]); - EXPECT_TRUE(out_edge_has_input_id(out, e20, 2)); - EXPECT_TRUE(out_edge_has_input_id(out, e20, 5)); - e24 = get_edge(out, v_out[2], v_out[4]); - EXPECT_TRUE(out_edge_has_input_id(out, e24, 3)); - e4i = get_edge(out, v_out[4], v_int); - EXPECT_TRUE(out_edge_has_input_id(out, e4i, 4)); - ei0 = get_edge(out, v_int, v_out[0]); - EXPECT_TRUE(out_edge_has_input_id(out, ei0, 4)); - f02i = get_face_tri(out, v_out[0], v_out[2], v_int); + int e01 = get_output_edge_index(out, v_out[0], v_out[1]); + int foff = out.face_edge_offset; + EXPECT_TRUE(output_edge_has_input_id(out, e01, foff + 1)); + int e1i = get_output_edge_index(out, v_out[1], v_int); + EXPECT_TRUE(output_edge_has_input_id(out, e1i, foff + 0)); + int ei2 = get_output_edge_index(out, v_int, v_out[2]); + EXPECT_TRUE(output_edge_has_input_id(out, ei2, foff + 0)); + int e20 = get_output_edge_index(out, v_out[2], v_out[0]); + EXPECT_TRUE(output_edge_has_input_id(out, e20, foff + 2)); + EXPECT_TRUE(output_edge_has_input_id(out, e20, 2 * foff + 2)); + int e24 = get_output_edge_index(out, v_out[2], v_out[4]); + EXPECT_TRUE(output_edge_has_input_id(out, e24, 2 * foff + 0)); + int e4i = get_output_edge_index(out, v_out[4], v_int); + EXPECT_TRUE(output_edge_has_input_id(out, e4i, 2 * foff + 1)); + int ei0 = get_output_edge_index(out, v_int, v_out[0]); + EXPECT_TRUE(output_edge_has_input_id(out, ei0, 2 * foff + 1)); + int f02i = get_output_tri_index(out, v_out[0], v_out[2], v_int); EXPECT_NE(f02i, -1); - EXPECT_TRUE(out_face_has_input_id(out, f02i, 0)); - EXPECT_TRUE(out_face_has_input_id(out, f02i, 1)); - f24i = get_face_tri(out, v_out[2], v_out[4], v_int); + EXPECT_TRUE(output_face_has_input_id(out, f02i, 0)); + EXPECT_TRUE(output_face_has_input_id(out, f02i, 1)); + int f24i = get_output_tri_index(out, v_out[2], v_out[4], v_int); EXPECT_NE(f24i, -1); - EXPECT_TRUE(out_face_has_input_id(out, f24i, 1)); - EXPECT_FALSE(out_face_has_input_id(out, f24i, 0)); - f10i = get_face_tri(out, v_out[1], v_out[0], v_int); + EXPECT_TRUE(output_face_has_input_id(out, f24i, 1)); + EXPECT_FALSE(output_face_has_input_id(out, f24i, 0)); + int f10i = get_output_tri_index(out, v_out[1], v_out[0], v_int); EXPECT_NE(f10i, -1); - EXPECT_TRUE(out_face_has_input_id(out, f10i, 0)); - EXPECT_FALSE(out_face_has_input_id(out, f10i, 1)); + EXPECT_TRUE(output_face_has_input_id(out, f10i, 0)); + EXPECT_FALSE(output_face_has_input_id(out, f10i, 1)); + } + if (DO_DRAW) { + graph_draw<T>("TwoFaceEdgeOverlap", out.vert, out.edge, out.face); } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); } -TEST(delaunay, TriInTri) +template<typename T> void triintri_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(6 0 2 -5.65685 0.0 1.41421 -5.83095 @@ -1051,19 +1137,18 @@ TEST(delaunay, TriInTri) 3 4 5 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->edges_len, 8); - EXPECT_EQ(out->faces_len, 3); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out.vert.size(), 6); + EXPECT_EQ(out.edge.size(), 8); + EXPECT_EQ(out.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("TriInTri", out.vert, out.edge, out.face); + } } -TEST(delaunay, DiamondInSquare) +template<typename T> void diamondinsquare_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(8 0 2 0.0 0.0 1.0 0.0 @@ -1076,19 +1161,19 @@ TEST(delaunay, DiamondInSquare) 0 1 2 3 4 5 6 7 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS_VALID_BMESH); - EXPECT_EQ(out->verts_len, 8); - EXPECT_EQ(out->edges_len, 10); - EXPECT_EQ(out->faces_len, 3); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS_VALID_BMESH); + EXPECT_EQ(out.vert.size(), 8); + EXPECT_EQ(out.edge.size(), 10); + EXPECT_EQ(out.face.size(), 3); + if (DO_DRAW) { + graph_draw<T>("DiamondInSquare", out.vert, out.edge, out.face); + } } -TEST(delaunay, DiamondInSquareWire) +template<typename T> void diamondinsquarewire_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(8 8 0 0.0 0.0 1.0 0.0 @@ -1107,67 +1192,19 @@ TEST(delaunay, DiamondInSquareWire) 6 7 7 4 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 8); - EXPECT_EQ(out->edges_len, 8); - EXPECT_EQ(out->faces_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} - -TEST(delaunay, TinyEdge) -{ - CDT_input in; - CDT_result *out; - /* An intersect with triangle would be at (0.8, 0.2). */ - const char *spec = R"(4 1 1 - 0.0 0.0 - 1.0 0.0 - 0.5 0.5 - 0.84 0.21 - 0 3 - 0 1 2 - )"; - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 5); - EXPECT_EQ(out->faces_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); -} -TEST(delaunay, TinyEdge2) -{ - CDT_input in; - CDT_result *out; - /* An intersect with triangle would be at (0.8, 0.2). */ - const char *spec = R"(6 1 1 - 0.0 0.0 - 0.2 -0.2 - 1.0 0.0 - 0.5 0.5 - 0.2 0.4 - 0.84 0.21 - 0 5 - 0 1 2 3 4 - )"; - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->edges_len, 7); - EXPECT_EQ(out->faces_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out.vert.size(), 8); + EXPECT_EQ(out.edge.size(), 8); + EXPECT_EQ(out.face.size(), 2); + if (DO_DRAW) { + graph_draw<T>("DiamondInSquareWire", out.vert, out.edge, out.face); + } } -TEST(delaunay, repeatededge) +template<typename T> void repeatedge_test() { - CDT_input in; - CDT_result *out; const char *spec = R"(5 3 0 0.0 0.0 0.0 1.0 @@ -1178,256 +1215,316 @@ TEST(delaunay, repeatededge) 2 3 2 3 )"; - fill_input_from_string(&in, spec); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->edges_len, 2); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); + + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out.edge.size(), 2); + if (DO_DRAW) { + graph_draw<T>("RepeatEdge", out.vert, out.edge, out.face); + } } -TEST(delaunay, NearSeg) +template<typename T> void repeattri_test() { - CDT_input in; - CDT_result *out; - int v[4], e0, e1, e2, i; - const char *spec = R"(4 2 0 + const char *spec = R"(3 0 2 0.0 0.0 1.0 0.0 - 0.25 0.09 - 0.25 1.0 - 0 1 - 2 3 + 0.5 1.0 + 0 1 2 + 0 1 2 )"; - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 3); - EXPECT_EQ(out->faces_len, 0); - if (out->edges_len == 3) { - for (i = 0; i < 4; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - e0 = get_edge(out, v[0], v[2]); - e1 = get_edge(out, v[2], v[1]); - e2 = get_edge(out, v[2], v[3]); - EXPECT_TRUE(out_edge_has_input_id(out, e0, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 1)); + CDT_input<T> in = fill_input_from_string<T>(spec); + CDT_result<T> out = delaunay_2d_calc(in, CDT_CONSTRAINTS); + EXPECT_EQ(out.edge.size(), 3); + EXPECT_EQ(out.face.size(), 1); + EXPECT_TRUE(output_face_has_input_id(out, 0, 0)); + EXPECT_TRUE(output_face_has_input_id(out, 0, 1)); + if (DO_DRAW) { + graph_draw<T>("RepeatTri", out.vert, out.edge, out.face); } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); } -TEST(delaunay, OverlapSegs) +TEST(delaunay_d, Empty) { - CDT_input in; - CDT_result *out; - int v[4], e0, e1, e2, i; - const char *spec = R"(4 2 0 - 0.0 0.0 - 1.0 0.0 - 0.4 0.09 - 1.4 0.09 - 0 1 - 2 3 - )"; + empty_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 4); - EXPECT_EQ(out->edges_len, 3); - EXPECT_EQ(out->faces_len, 0); - if (out->edges_len == 3) { - for (i = 0; i < 4; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - e0 = get_edge(out, v[0], v[2]); - e1 = get_edge(out, v[2], v[1]); - e2 = get_edge(out, v[1], v[3]); - EXPECT_TRUE(out_edge_has_input_id(out, e0, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 1)); - } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, OnePt) +{ + onept_test<double>(); } -TEST(delaunay, NearSegWithDup) +TEST(delaunay_d, TwoPt) { - CDT_input in; - CDT_result *out; - int v[5], e0, e1, e2, e3, i; - const char *spec = R"(5 3 0 - 0.0 0.0 - 1.0 0.0 - 0.25 0.09 - 0.25 1.0 - 0.75 0.09 - 0 1 - 2 3 - 2 4 - )"; + twopt_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 5); - EXPECT_EQ(out->edges_len, 4); - EXPECT_EQ(out->faces_len, 0); - if (out->edges_len == 5) { - for (i = 0; i < 5; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - e0 = get_edge(out, v[0], v[2]); - e1 = get_edge(out, v[2], v[4]); - e2 = get_edge(out, v[4], v[1]); - e3 = get_edge(out, v[3], v[2]); - EXPECT_TRUE(out_edge_has_input_id(out, e0, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 2)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e3, 1)); - } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, ThreePt) +{ + threept_test<double>(); } -TEST(delaunay, TwoNearSeg) +TEST(delaunay_d, MixedPts) { - CDT_input in; - CDT_result *out; - int v[5], e0, e1, e2, e3, e4, i; - const char *spec = R"(5 3 0 - 0.0 0.0 - 1.0 0.0 - 0.25 0.09 - 0.25 1.0 - 0.75 0.09 - 0 1 - 3 2 - 3 4 - )"; + mixedpts_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.1; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 5); - EXPECT_EQ(out->edges_len, 5); - EXPECT_EQ(out->faces_len, 1); - if (out->edges_len == 5) { - for (i = 0; i < 5; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - e0 = get_edge(out, v[0], v[2]); - e1 = get_edge(out, v[2], v[4]); - e2 = get_edge(out, v[4], v[1]); - e3 = get_edge(out, v[3], v[2]); - e4 = get_edge(out, v[3], v[4]); - EXPECT_TRUE(out_edge_has_input_id(out, e0, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 0)); - EXPECT_TRUE(out_edge_has_input_id(out, e3, 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e4, 2)); - } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, Quad0) +{ + quad0_test<double>(); } -TEST(delaunay, FaceNearSegs) +TEST(delaunay_d, Quad1) { - CDT_input in; - CDT_result *out; - int v[9], e0, e1, e2, e3, i; - const char *spec = R"(8 1 2 - 0.0 0.0 - 2.0 0.0 - 1.0 1.0 - 0.21 0.2 - 1.79 0.2 - 0.51 0.5 - 1.49 0.5 - 1.0 0.19 - 2 7 - 0 1 2 - 3 4 6 5 - )"; + quad1_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.05; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 9); - EXPECT_EQ(out->edges_len, 13); - EXPECT_EQ(out->faces_len, 5); - if (out->verts_len == 9 && out->edges_len == 13) { - for (i = 0; i < 8; i++) { - v[i] = get_output_vert_index(out, i); - EXPECT_NE(v[i], -1); - } - v[8] = 8; - e0 = get_edge(out, v[0], v[1]); - e1 = get_edge(out, v[4], v[6]); - e2 = get_edge(out, v[3], v[0]); - e3 = get_edge(out, v[2], v[8]); - - EXPECT_TRUE(out_edge_has_input_id(out, e0, 1)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 2)); - EXPECT_TRUE(out_edge_has_input_id(out, e1, 5)); - EXPECT_TRUE(out_edge_has_input_id(out, e2, 3)); - EXPECT_TRUE(out_edge_has_input_id(out, e3, 0)); - } - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, Quad2) +{ + quad2_test<double>(); } -TEST(delaunay, ChainNearIntersects) +TEST(delaunay_d, Quad3) { - CDT_input in; - CDT_result *out; - const char *spec = R"(6 10 0 - 0.8 1.25 - 1.25 0.75 - 3.25 1.25 - 5.0 1.9 - 2.5 4.0 - 1.0 2.25 - 0 1 - 1 2 - 2 3 - 3 4 - 4 5 - 5 0 - 0 2 - 5 2 - 4 2 - 1 3 - )"; + quad3_test<double>(); +} + +TEST(delaunay_d, Quad4) +{ + quad4_test<double>(); +} + +TEST(delaunay_d, LineInSquare) +{ + lineinsquare_test<double>(); +} + +TEST(delaunay_d, CrossSegs) +{ + crosssegs_test<double>(); +} + +TEST(delaunay_d, DiamondCross) +{ + diamondcross_test<double>(); +} + +TEST(delaunay_d, TwoDiamondsCross) +{ + twodiamondscross_test<double>(); +} + +TEST(delaunay_d, ManyCross) +{ + manycross_test<double>(); +} + +TEST(delaunay_d, TwoFace) +{ + twoface_test<double>(); +} + +TEST(delaunay_d, TwoFace2) +{ + twoface2_test<double>(); +} + +TEST(delaunay_d, OverlapFaces) +{ + overlapfaces_test<double>(); +} - fill_input_from_string(&in, spec); - in.epsilon = 0.05; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 9); - EXPECT_EQ(out->edges_len, 16); - BLI_delaunay_2d_cdt_free(out); - in.epsilon = 0.11; - /* The chaining we want to test happens prematurely if modify input. */ - in.skip_input_modify = true; - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - EXPECT_EQ(out->verts_len, 6); - EXPECT_EQ(out->edges_len, 9); - free_spec_arrays(&in); - BLI_delaunay_2d_cdt_free(out); +TEST(delaunay_d, TwoSquaresOverlap) +{ + twosquaresoverlap_test<double>(); +} + +TEST(delaunay_d, TwoFaceEdgeOverlap) +{ + twofaceedgeoverlap_test<double>(); +} + +TEST(delaunay_d, TriInTri) +{ + triintri_test<double>(); +} + +TEST(delaunay_d, DiamondInSquare) +{ + diamondinsquare_test<double>(); +} + +TEST(delaunay_d, DiamondInSquareWire) +{ + diamondinsquarewire_test<double>(); +} + +TEST(delaunay_d, RepeatEdge) +{ + repeatedge_test<double>(); +} + +TEST(delaunay_d, RepeatTri) +{ + repeattri_test<double>(); +} + +# ifdef WITH_GMP +TEST(delaunay_m, Empty) +{ + empty_test<mpq_class>(); +} + +TEST(delaunay_m, OnePt) +{ + onept_test<mpq_class>(); +} +TEST(delaunay_m, TwoPt) +{ + twopt_test<mpq_class>(); +} + +TEST(delaunay_m, ThreePt) +{ + threept_test<mpq_class>(); +} + +TEST(delaunay_m, MixedPts) +{ + mixedpts_test<mpq_class>(); +} + +TEST(delaunay_m, Quad0) +{ + quad0_test<mpq_class>(); +} + +TEST(delaunay_m, Quad1) +{ + quad1_test<mpq_class>(); +} + +TEST(delaunay_m, Quad2) +{ + quad2_test<mpq_class>(); +} + +TEST(delaunay_m, Quad3) +{ + quad3_test<mpq_class>(); +} + +TEST(delaunay_m, Quad4) +{ + quad4_test<mpq_class>(); +} + +TEST(delaunay_m, LineInSquare) +{ + lineinsquare_test<mpq_class>(); +} + +TEST(delaunay_m, CrossSegs) +{ + crosssegs_test<mpq_class>(); +} + +TEST(delaunay_m, DiamondCross) +{ + diamondcross_test<mpq_class>(); +} + +TEST(delaunay_m, TwoDiamondsCross) +{ + twodiamondscross_test<mpq_class>(); +} + +TEST(delaunay_m, ManyCross) +{ + manycross_test<mpq_class>(); +} + +TEST(delaunay_m, TwoFace) +{ + twoface_test<mpq_class>(); +} + +TEST(delaunay_m, TwoFace2) +{ + twoface2_test<mpq_class>(); +} + +TEST(delaunay_m, OverlapFaces) +{ + overlapfaces_test<mpq_class>(); +} + +TEST(delaunay_m, TwoSquaresOverlap) +{ + twosquaresoverlap_test<mpq_class>(); +} + +TEST(delaunay_m, TwoFaceEdgeOverlap) +{ + twofaceedgeoverlap_test<mpq_class>(); +} + +TEST(delaunay_m, TriInTri) +{ + triintri_test<mpq_class>(); +} + +TEST(delaunay_m, DiamondInSquare) +{ + diamondinsquare_test<mpq_class>(); +} + +TEST(delaunay_m, DiamondInSquareWire) +{ + diamondinsquarewire_test<mpq_class>(); +} + +TEST(delaunay_m, RepeatEdge) +{ + repeatedge_test<mpq_class>(); +} + +TEST(delaunay_m, RepeatTri) +{ + repeattri_test<mpq_class>(); +} +# endif + +#endif + +#if DO_C_TESTS + +TEST(delaunay_d, CintTwoFace) +{ + float vert_coords[][2] = { + {0.0, 0.0}, {1.0, 0.0}, {0.5, 1.0}, {1.1, 1.0}, {1.1, 0.0}, {1.6, 1.0}}; + int faces[] = {0, 1, 2, 3, 4, 5}; + int faces_len[] = {3, 3}; + int faces_start[] = {0, 3}; + + ::CDT_input input; + input.verts_len = 6; + input.edges_len = 0; + input.faces_len = 2; + input.vert_coords = vert_coords; + input.edges = NULL; + input.faces = faces; + input.faces_len_table = faces_len; + input.faces_start_table = faces_start; + input.epsilon = 1e-5f; + ::CDT_result *output = BLI_delaunay_2d_cdt_calc(&input, CDT_FULL); + BLI_delaunay_2d_cdt_free(output); } #endif #if DO_RANDOM_TESTS + enum { RANDOM_PTS, RANDOM_SEGS, @@ -1437,342 +1534,323 @@ enum { RANDOM_TRI_BETWEEN_CIRCLES, }; -# define DO_TIMING -static void rand_delaunay_test(int test_kind, - int start_lg_size, - int max_lg_size, - int reps_per_size, - double param, - CDT_output_type otype) -{ - CDT_input in; - CDT_result *out; - int lg_size, size, rep, i, j, size_max, npts_max, nedges_max, nfaces_max, npts, nedges, nfaces; - int ia, ib, ic; - float(*p)[2]; - int(*e)[2]; - int *faces, *faces_start_table, *faces_len_table; - double start_angle, angle_delta, angle1, angle2, angle3; - float orient; - double tstart; - double *times; - RNG *rng; - - rng = BLI_rng_new(0); - e = NULL; - faces = NULL; - faces_start_table = NULL; - faces_len_table = NULL; - nedges_max = 0; - nfaces_max = 0; - - /* Set up npts, nedges, nfaces, and allocate needed arrays at max length needed. */ - size_max = 1 << max_lg_size; - switch (test_kind) { - case RANDOM_PTS: - case RANDOM_SEGS: - case RANDOM_POLY: - npts_max = size_max; - if (test_kind == RANDOM_SEGS) { - nedges_max = npts_max - 1; - } - else if (test_kind == RANDOM_POLY) { - nedges_max = npts_max; - } - break; - - case RANDOM_TILTED_GRID: - /* A 'size' x 'size' grid of points, tilted by angle 'param'. - * Edges will go from left ends to right ends and tops to bottoms, so 2 x size of them. - * Depending on epsilon, the vertical-ish edges may or may not go through the intermediate - * vertices, but the horizontal ones always should. - */ - npts_max = size_max * size_max; - nedges_max = 2 * size_max; - break; - - case RANDOM_CIRCLE: - /* A circle with 'size' points, a random start angle, and equal spacing thereafter. - * Will be input as one face. - */ - npts_max = size_max; - nfaces_max = 1; - break; - - case RANDOM_TRI_BETWEEN_CIRCLES: - /* A set of 'size' triangles, each has two random points on the unit circle, - * and the third point is a random point on the circle with radius 'param'. - * Each triangle will be input as a face. - */ - npts_max = 3 * size_max; - nfaces_max = size_max; - break; - - default: - fprintf(stderr, "unknown random delaunay test kind\n"); - return; - } - p = (float(*)[2])MEM_malloc_arrayN(npts_max, 2 * sizeof(float), __func__); - if (nedges_max > 0) { - e = (int(*)[2])MEM_malloc_arrayN(nedges_max, 2 * sizeof(int), __func__); - } - if (nfaces_max > 0) { - faces_start_table = (int *)MEM_malloc_arrayN(nfaces_max, sizeof(int), __func__); - faces_len_table = (int *)MEM_malloc_arrayN(nfaces_max, sizeof(int), __func__); - faces = (int *)MEM_malloc_arrayN(npts_max, sizeof(int), __func__); - } - - times = (double *)MEM_malloc_arrayN(max_lg_size + 1, sizeof(double), __func__); +template<typename T> +void rand_delaunay_test(int test_kind, + int start_lg_size, + int max_lg_size, + int reps_per_size, + double param, + CDT_output_type otype) +{ + constexpr bool print_timing = true; + RNG *rng = BLI_rng_new(0); + Array<double> times(max_lg_size + 1); /* For powers of 2 sizes up to max_lg_size power of 2. */ - for (lg_size = start_lg_size; lg_size <= max_lg_size; lg_size++) { - size = 1 << lg_size; - nedges = 0; - nfaces = 0; + for (int lg_size = start_lg_size; lg_size <= max_lg_size; ++lg_size) { + int size = 1 << lg_size; times[lg_size] = 0.0; if (size == 1 && test_kind != RANDOM_PTS) { continue; } /* Do 'rep' repetitions. */ - for (rep = 0; rep < reps_per_size; rep++) { + for (int rep = 0; rep < reps_per_size; ++rep) { + /* First use test type and size to set npts, nedges, and nfaces. */ + int npts = 0; + int nedges = 0; + int nfaces = 0; + std::string test_label; + switch (test_kind) { + case RANDOM_PTS: { + npts = size; + test_label = std::to_string(npts) + "Random points"; + } break; + case RANDOM_SEGS: { + npts = size; + nedges = npts - 1; + test_label = std::to_string(nedges) + "Random edges"; + } break; + case RANDOM_POLY: { + npts = size; + nedges = npts; + test_label = "Random poly with " + std::to_string(nedges) + " edges"; + } break; + case RANDOM_TILTED_GRID: { + /* A 'size' x 'size' grid of points, tilted by angle 'param'. + * Edges will go from left ends to right ends and tops to bottoms, + * so 2 x size of them. + * Depending on epsilon, the vertical-ish edges may or may not go + * through the intermediate vertices, but the horizontal ones always should. + * 'param' is slope of tilt of vertical lines. + */ + npts = size * size; + nedges = 2 * size; + test_label = "Tilted grid " + std::to_string(npts) + "x" + std::to_string(npts) + + " (tilt=" + std::to_string(param) + ")"; + } break; + case RANDOM_CIRCLE: { + /* A circle with 'size' points, a random start angle, + * and equal spacing thereafter. Will be input as one face. + */ + npts = size; + nfaces = 1; + test_label = "Circle with " + std::to_string(npts) + " points"; + } break; + case RANDOM_TRI_BETWEEN_CIRCLES: { + /* A set of 'size' triangles, each has two random points on the unit circle, + * and the third point is a random point on the circle with radius 'param'. + * Each triangle will be input as a face. + */ + npts = 3 * size; + nfaces = size; + test_label = "Random " + std::to_string(nfaces) + + " triangles between circles (inner radius=" + std::to_string(param) + ")"; + } break; + default: + std::cout << "unknown delaunay test type\n"; + return; + } + if (otype != CDT_FULL) { + if (otype == CDT_INSIDE) { + test_label += " (inside)"; + } + else if (otype == CDT_CONSTRAINTS) { + test_label += " (constraints)"; + } + else if (otype == CDT_CONSTRAINTS_VALID_BMESH) { + test_label += " (valid bmesh)"; + } + } + + CDT_input<T> in; + in.vert = Array<vec2<T>>(npts); + if (nedges > 0) { + in.edge = Array<std::pair<int, int>>(nedges); + } + if (nfaces > 0) { + in.face = Array<Vector<int>>(nfaces); + } + /* Make vertices and edges or faces. */ switch (test_kind) { case RANDOM_PTS: case RANDOM_SEGS: - case RANDOM_POLY: - npts = size; - if (test_kind == RANDOM_SEGS) { - nedges = npts - 1; - } - else if (test_kind == RANDOM_POLY) { - nedges = npts; - } - for (i = 0; i < size; i++) { - p[i][0] = (float)BLI_rng_get_double(rng); /* will be in range in [0,1) */ - p[i][1] = (float)BLI_rng_get_double(rng); + case RANDOM_POLY: { + for (int i = 0; i < size; i++) { + in.vert[i][0] = T(BLI_rng_get_double(rng)); /* will be in range in [0,1) */ + in.vert[i][1] = T(BLI_rng_get_double(rng)); if (test_kind != RANDOM_PTS) { if (i > 0) { - e[i - 1][0] = i - 1; - e[i - 1][1] = i; + in.edge[i - 1].first = i - 1; + in.edge[i - 1].second = i; } } } if (test_kind == RANDOM_POLY) { - e[size - 1][0] = size - 1; - e[size - 1][1] = 0; + in.edge[size - 1].first = size - 1; + in.edge[size - 1].second = 0; } - break; + } break; - case RANDOM_TILTED_GRID: - /* 'param' is slope of tilt of vertical lines. */ - npts = size * size; - nedges = 2 * size; - for (i = 0; i < size; i++) { - for (j = 0; j < size; j++) { - p[i * size + j][0] = i * param + j; - p[i * size + j][1] = i; + case RANDOM_TILTED_GRID: { + for (int i = 0; i < size; ++i) { + for (int j = 0; j < size; ++j) { + in.vert[i * size + j][0] = T(i * param + j); + in.vert[i * size + j][1] = T(i); } } - for (i = 0; i < size; i++) { + for (int i = 0; i < size; ++i) { /* Horizontal edges: connect p(i,0) to p(i,size-1). */ - e[i][0] = i * size; - e[i][1] = i * size + size - 1; + in.edge[i].first = i * size; + in.edge[i].second = i * size + size - 1; /* Vertical edges: conntect p(0,i) to p(size-1,i). */ - e[size + i][0] = i; - e[size + i][1] = (size - 1) * size + i; + in.edge[size + i].first = i; + in.edge[size + i].second = (size - 1) * size + i; } - break; - - case RANDOM_CIRCLE: - npts = size; - nfaces = 1; - faces_start_table[0] = 0; - faces_len_table[0] = npts; - start_angle = BLI_rng_get_double(rng) * 2.0 * M_PI; - angle_delta = 2.0 * M_PI / size; - for (i = 0; i < size; i++) { - p[i][0] = (float)cos(start_angle + i * angle_delta); - p[i][1] = (float)sin(start_angle + i * angle_delta); - faces[i] = i; + } break; + + case RANDOM_CIRCLE: { + double start_angle = BLI_rng_get_double(rng) * 2.0 * M_PI; + double angle_delta = 2.0 * M_PI / size; + for (int i = 0; i < size; i++) { + in.vert[i][0] = T(cos(start_angle + i * angle_delta)); + in.vert[i][1] = T(sin(start_angle + i * angle_delta)); + in.face[0].append(i); } - break; + } break; - case RANDOM_TRI_BETWEEN_CIRCLES: - npts = 3 * size; - nfaces = size; - for (i = 0; i < size; i++) { + case RANDOM_TRI_BETWEEN_CIRCLES: { + for (int i = 0; i < size; i++) { /* Get three random angles in [0, 2pi). */ - angle1 = BLI_rng_get_double(rng) * 2.0 * M_PI; - angle2 = BLI_rng_get_double(rng) * 2.0 * M_PI; - angle3 = BLI_rng_get_double(rng) * 2.0 * M_PI; - ia = 3 * i; - ib = 3 * i + 1; - ic = 3 * i + 2; - p[ia][0] = (float)cos(angle1); - p[ia][1] = (float)sin(angle1); - p[ib][0] = (float)cos(angle2); - p[ib][1] = (float)sin(angle2); - p[ic][0] = (float)(param * cos(angle3)); - p[ic][1] = (float)(param * sin(angle3)); - faces_start_table[i] = 3 * i; - faces_len_table[i] = 3; + double angle1 = BLI_rng_get_double(rng) * 2.0 * M_PI; + double angle2 = BLI_rng_get_double(rng) * 2.0 * M_PI; + double angle3 = BLI_rng_get_double(rng) * 2.0 * M_PI; + int ia = 3 * i; + int ib = 3 * i + 1; + int ic = 3 * i + 2; + in.vert[ia][0] = T(cos(angle1)); + in.vert[ia][1] = T(sin(angle1)); + in.vert[ib][0] = T(cos(angle2)); + in.vert[ib][1] = T(sin(angle2)); + in.vert[ic][0] = T((param * cos(angle3))); + in.vert[ic][1] = T((param * sin(angle3))); /* Put the coordinates in ccw order. */ - faces[ia] = ia; - orient = (p[ia][0] - p[ic][0]) * (p[ib][1] - p[ic][1]) - - (p[ib][0] - p[ic][0]) * (p[ia][1] - p[ic][1]); - if (orient >= 0.0f) { - faces[ib] = ib; - faces[ic] = ic; + in.face[i].append(ia); + int orient = vec2<T>::orient2d(in.vert[ia], in.vert[ib], in.vert[ic]); + if (orient >= 0) { + in.face[i].append(ib); + in.face[i].append(ic); } else { - faces[ib] = ic; - faces[ic] = ib; + in.face[i].append(ic); + in.face[i].append(ib); } } - break; - } - fill_input_verts(&in, p, npts); - if (nedges > 0) { - add_input_edges(&in, e, nedges); - } - if (nfaces > 0) { - add_input_faces(&in, faces, faces_start_table, faces_len_table, nfaces); + } break; } /* Run the test. */ - tstart = PIL_check_seconds_timer(); - out = BLI_delaunay_2d_cdt_calc(&in, otype); - EXPECT_NE(out->verts_len, 0); - BLI_delaunay_2d_cdt_free(out); + double tstart = PIL_check_seconds_timer(); + CDT_result<T> out = delaunay_2d_calc(in, otype); + EXPECT_NE(out.vert.size(), 0); times[lg_size] += PIL_check_seconds_timer() - tstart; + if (DO_DRAW) { + graph_draw<T>(test_label, out.vert, out.edge, out.face); + } } } -# ifdef DO_TIMING - fprintf(stderr, "size,time\n"); - for (lg_size = 0; lg_size <= max_lg_size; lg_size++) { - fprintf(stderr, "%d,%f\n", 1 << lg_size, times[lg_size] / reps_per_size); - } -# endif - MEM_freeN(p); - if (e) { - MEM_freeN(e); - } - if (faces) { - MEM_freeN(faces); - MEM_freeN(faces_start_table); - MEM_freeN(faces_len_table); + if (print_timing) { + std::cout << "\nsize,time\n"; + for (int lg_size = 0; lg_size <= max_lg_size; lg_size++) { + int size = 1 << lg_size; + std::cout << size << "," << times[lg_size] << "\n"; + } } - MEM_freeN(times); BLI_rng_free(rng); } -TEST(delaunay, randompts) +TEST(delaunay_d, RandomPts) { - rand_delaunay_test(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL); } -TEST(delaunay, randomsegs) +TEST(delaunay_d, RandomSegs) { - rand_delaunay_test(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL); } -TEST(delaunay, randompoly) +TEST(delaunay_d, RandomPoly) { - rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL); } -TEST(delaunay, randompoly_inside) +TEST(delaunay_d, RandomPolyConstraints) { - rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE); + rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS); } -TEST(delaunay, randompoly_constraints) +TEST(delaunay_d, RandomPolyValidBmesh) { - rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS); + rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH); } -TEST(delaunay, randompoly_validbmesh) +TEST(delaunay_d, Grid) { - rand_delaunay_test(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH); + rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL); } -TEST(delaunay, grid) +TEST(delaunay_d, TiltedGridA) { - rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL); } -TEST(delaunay, tilted_grid_a) +TEST(delaunay_d, TiltedGridB) { - rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL); } -TEST(delaunay, tilted_grid_b) +TEST(delaunay_d, RandomCircle) { - rand_delaunay_test(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL); + rand_delaunay_test<double>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL); } -TEST(delaunay, randomcircle) +TEST(delaunay_d, RandomTrisCircle) { - rand_delaunay_test(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL); + rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL); } -TEST(delaunay, random_tris_circle) +TEST(delaunay_d, RandomTrisCircleB) { - rand_delaunay_test(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL); + rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL); } -TEST(delaunay, random_tris_circle_b) +# ifdef WITH_GMP +TEST(delaunay_m, RandomPts) { - rand_delaunay_test(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL); + rand_delaunay_test<mpq_class>(RANDOM_PTS, 0, 7, 1, 0.0, CDT_FULL); } -#endif -#if DO_FILE_TESTS -/* For manually testing performance by timing a large number of points from a - * file. See fill_input_from_file for file format. - */ -static void points_from_file_test(const char *filename) +TEST(delaunay_m, RandomSegs) { - CDT_input in; - CDT_result *out; - double tstart; + rand_delaunay_test<mpq_class>(RANDOM_SEGS, 1, 7, 1, 0.0, CDT_FULL); +} - fill_input_from_file(&in, filename); - tstart = PIL_check_seconds_timer(); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_FULL); - fprintf(stderr, "time to triangulate=%f seconds\n", PIL_check_seconds_timer() - tstart); - BLI_delaunay_2d_cdt_free(out); - free_spec_arrays(&in); +TEST(delaunay_m, RandomPoly) +{ + rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_FULL); } -# if 0 -TEST(delaunay, debug) +TEST(delaunay_d, RandomPolyInside) { - CDT_input in; - CDT_result *out; - fill_input_from_file(&in, "/tmp/cdtinput.txt"); - out = BLI_delaunay_2d_cdt_calc(&in, CDT_CONSTRAINTS); - BLI_delaunay_2d_cdt_free(out); - free_spec_arrays(&in); + rand_delaunay_test<double>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE); +} + +TEST(delaunay_m, RandomPolyInside) +{ + rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_INSIDE); +} + +TEST(delaunay_m, RandomPolyConstraints) +{ + rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS); } -# endif -# if 1 -# define POINTFILEROOT "/tmp/" +TEST(delaunay_m, RandomPolyValidBmesh) +{ + rand_delaunay_test<mpq_class>(RANDOM_POLY, 1, 7, 1, 0.0, CDT_CONSTRAINTS_VALID_BMESH); +} -TEST(delaunay, terrain1) +TEST(delaunay_m, Grid) { - points_from_file_test(POINTFILEROOT "points1.txt"); + rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.0, CDT_FULL); } -TEST(delaunay, terrain2) +TEST(delaunay_m, TiltedGridA) { - points_from_file_test(POINTFILEROOT "points2.txt"); + rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 1.0, CDT_FULL); } -TEST(delaunay, terrain3) +TEST(delaunay_m, TiltedGridB) { - points_from_file_test(POINTFILEROOT "points3.txt"); + rand_delaunay_test<mpq_class>(RANDOM_TILTED_GRID, 1, 6, 1, 0.01, CDT_FULL); +} + +TEST(delaunay_m, RandomCircle) +{ + rand_delaunay_test<mpq_class>(RANDOM_CIRCLE, 1, 7, 1, 0.0, CDT_FULL); +} + +TEST(delaunay_m, RandomTrisCircle) +{ + rand_delaunay_test<mpq_class>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 0.25, CDT_FULL); +} + +TEST(delaunay_m, RandomTrisCircleB) +{ + rand_delaunay_test<double>(RANDOM_TRI_BETWEEN_CIRCLES, 1, 6, 1, 1e-4, CDT_FULL); } # endif + #endif + +} // namespace blender::meshintersect diff --git a/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh b/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh new file mode 100644 index 00000000000..91270767a25 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_exception_safety_test_utils.hh @@ -0,0 +1,102 @@ +#include "BLI_hash.hh" +#include "BLI_utildefines.h" +#include "MEM_guardedalloc.h" +#include "testing/testing.h" + +namespace blender::tests { + +class ExceptionThrower { + private: + /* Use some random values that are unlikely to exist at the memory location already. */ + static constexpr uint32_t is_alive_state = 0x21254634; + static constexpr uint32_t is_destructed_state = 0xFA4BC327; + + uint32_t state_; + + /* Make use of leak detector to check if this value has been destructed. */ + void *my_memory_; + + public: + mutable bool throw_during_copy; + mutable bool throw_during_move; + /* Used for hashing and comparing. */ + int value; + + ExceptionThrower(int value = 0) + : state_(is_alive_state), + my_memory_(MEM_mallocN(1, AT)), + throw_during_copy(false), + throw_during_move(false), + value(value) + { + } + + ExceptionThrower(const ExceptionThrower &other) : ExceptionThrower(other.value) + { + EXPECT_EQ(other.state_, is_alive_state); + if (other.throw_during_copy) { + throw std::runtime_error("throwing during copy, as requested"); + } + } + + ExceptionThrower(ExceptionThrower &&other) : ExceptionThrower(other.value) + { + EXPECT_EQ(other.state_, is_alive_state); + if (other.throw_during_move) { + throw std::runtime_error("throwing during move, as requested"); + } + } + + ExceptionThrower &operator=(const ExceptionThrower &other) + { + EXPECT_EQ(other.state_, is_alive_state); + if (throw_during_copy || other.throw_during_copy) { + throw std::runtime_error("throwing during copy, as requested"); + } + value = other.value; + return *this; + } + + ExceptionThrower &operator=(ExceptionThrower &&other) + { + EXPECT_EQ(other.state_, is_alive_state); + if (throw_during_move || other.throw_during_move) { + throw std::runtime_error("throwing during move, as requested"); + } + value = other.value; + return *this; + } + + ~ExceptionThrower() + { + const char *message = ""; + if (state_ != is_alive_state) { + if (state_ == is_destructed_state) { + message = "Trying to destruct an already destructed instance."; + } + else { + message = "Trying to destruct an uninitialized instance."; + } + } + EXPECT_EQ(state_, is_alive_state) << message; + state_ = is_destructed_state; + MEM_freeN(my_memory_); + } + + uint64_t hash() const + { + return static_cast<uint64_t>(value); + } + + friend bool operator==(const ExceptionThrower &a, const ExceptionThrower &b) + { + return a.value == b.value; + } + + friend bool operator!=(const ExceptionThrower &a, const ExceptionThrower &b) + { + return !(a == b); + } +}; + +} // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_map_test.cc b/source/blender/blenlib/tests/BLI_map_test.cc index fe7b0f01279..7b4a484e736 100644 --- a/source/blender/blenlib/tests/BLI_map_test.cc +++ b/source/blender/blenlib/tests/BLI_map_test.cc @@ -1,5 +1,6 @@ /* Apache License, Version 2.0 */ +#include "BLI_exception_safety_test_utils.hh" #include "BLI_map.hh" #include "BLI_rand.h" #include "BLI_set.hh" @@ -479,6 +480,72 @@ TEST(map, ForeachItem) EXPECT_EQ(keys.first_index_of(1), values.first_index_of(8)); } +TEST(map, CopyConstructorExceptions) +{ + using MapType = Map<ExceptionThrower, ExceptionThrower>; + MapType map; + map.add(2, 2); + map.add(4, 4); + map.lookup(2).throw_during_copy = true; + EXPECT_ANY_THROW({ MapType map_copy(map); }); +} + +TEST(map, MoveConstructorExceptions) +{ + using MapType = Map<ExceptionThrower, ExceptionThrower>; + MapType map; + map.add(1, 1); + map.add(2, 2); + map.lookup(1).throw_during_move = true; + EXPECT_ANY_THROW({ MapType map_moved(std::move(map)); }); + map.add(5, 5); +} + +TEST(map, AddNewExceptions) +{ + Map<ExceptionThrower, ExceptionThrower> map; + ExceptionThrower key1 = 1; + key1.throw_during_copy = true; + ExceptionThrower value1; + EXPECT_ANY_THROW({ map.add_new(key1, value1); }); + EXPECT_EQ(map.size(), 0); + ExceptionThrower key2 = 2; + ExceptionThrower value2; + value2.throw_during_copy = true; + EXPECT_ANY_THROW({ map.add_new(key2, value2); }); +} + +TEST(map, ReserveExceptions) +{ + Map<ExceptionThrower, ExceptionThrower> map; + map.add(3, 3); + map.add(5, 5); + map.add(2, 2); + map.lookup(2).throw_during_move = true; + EXPECT_ANY_THROW({ map.reserve(100); }); + map.add(1, 1); + map.add(5, 5); +} + +TEST(map, PopExceptions) +{ + Map<ExceptionThrower, ExceptionThrower> map; + map.add(3, 3); + map.lookup(3).throw_during_move = true; + EXPECT_ANY_THROW({ map.pop(3); }); + EXPECT_EQ(map.size(), 1); + map.add(1, 1); + EXPECT_EQ(map.size(), 2); +} + +TEST(map, AddOrModifyExceptions) +{ + Map<ExceptionThrower, ExceptionThrower> map; + auto create_fn = [](ExceptionThrower *UNUSED(v)) { throw std::runtime_error(""); }; + auto modify_fn = [](ExceptionThrower *UNUSED(v)) {}; + EXPECT_ANY_THROW({ map.add_or_modify(3, create_fn, modify_fn); }); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/source/blender/blenlib/tests/BLI_memory_utils_test.cc b/source/blender/blenlib/tests/BLI_memory_utils_test.cc index f3cb02b63d7..fcef2f8688a 100644 --- a/source/blender/blenlib/tests/BLI_memory_utils_test.cc +++ b/source/blender/blenlib/tests/BLI_memory_utils_test.cc @@ -7,6 +7,7 @@ namespace blender::tests { +namespace { struct MyValue { static inline int alive = 0; @@ -33,6 +34,7 @@ struct MyValue { alive--; } }; +} // namespace TEST(memory_utils, DefaultConstructN_ActuallyCallsConstructor) { diff --git a/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc b/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc new file mode 100644 index 00000000000..b212ddb8e63 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_mesh_boolean_test.cc @@ -0,0 +1,910 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include <fstream> +#include <iostream> +#include <sstream> + +#include "MEM_guardedalloc.h" + +#include "BLI_array.hh" +#include "BLI_map.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mesh_boolean.hh" +#include "BLI_mpq3.hh" +#include "BLI_vector.hh" + +#ifdef WITH_GMP +namespace blender::meshintersect::tests { + +constexpr bool DO_OBJ = false; + +/* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */ +class IMeshBuilder { + public: + IMesh imesh; + IMeshArena arena; + + /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */ + static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */ + + static int edge_index(int face_index, int facepos) + { + return face_index * MAX_FACE_LEN + facepos; + } + + static std::pair<int, int> face_and_pos_for_edge_index(int e_index) + { + return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN); + } + + /* + * Spec should have form: + * #verts #faces + * mpq_class mpq_class mpq_clas [#verts lines] + * int int int ... [#faces lines; indices into verts for given face] + */ + IMeshBuilder(const char *spec) + { + std::istringstream ss(spec); + std::string line; + getline(ss, line); + std::istringstream hdrss(line); + int nv, nf; + hdrss >> nv >> nf; + if (nv == 0 || nf == 0) { + return; + } + arena.reserve(nv, nf); + Vector<const Vert *> verts; + Vector<Face *> faces; + bool spec_ok = true; + int v_index = 0; + while (v_index < nv && spec_ok && getline(ss, line)) { + std::istringstream iss(line); + mpq_class p0; + mpq_class p1; + mpq_class p2; + iss >> p0 >> p1 >> p2; + spec_ok = !iss.fail(); + verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index)); + ++v_index; + } + if (v_index != nv) { + spec_ok = false; + } + int f_index = 0; + while (f_index < nf && spec_ok && getline(ss, line)) { + std::istringstream fss(line); + Vector<const Vert *> face_verts; + Vector<int> edge_orig; + int fpos = 0; + while (spec_ok && fss >> v_index) { + if (v_index < 0 || v_index >= nv) { + spec_ok = false; + continue; + } + face_verts.append(verts[v_index]); + edge_orig.append(edge_index(f_index, fpos)); + ++fpos; + } + Face *facep = arena.add_face(face_verts, f_index, edge_orig); + faces.append(facep); + ++f_index; + } + if (f_index != nf) { + spec_ok = false; + } + if (!spec_ok) { + std::cout << "Bad spec: " << spec; + return; + } + imesh = IMesh(faces); + } +}; + +static int all_shape_zero(int UNUSED(t)) +{ + return 0; +} + +TEST(boolean_trimesh, Empty) +{ + IMeshArena arena; + IMesh in; + IMesh out = boolean_trimesh(in, BoolOpType::None, 1, all_shape_zero, true, &arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 0); + EXPECT_EQ(out.face_size(), 0); +} + +TEST(boolean_trimesh, TetTetTrimesh) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::None, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 11); + EXPECT_EQ(out.face_size(), 20); + if (DO_OBJ) { + write_obj_mesh(out, "tettet_tm"); + } + + IMeshBuilder mb2(spec); + IMesh out2 = boolean_trimesh(mb2.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb2.arena); + out2.populate_vert(); + EXPECT_EQ(out2.vert_size(), 10); + EXPECT_EQ(out2.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out2, "tettet_union_tm"); + } + + IMeshBuilder mb3(spec); + IMesh out3 = boolean_trimesh( + mb3.imesh, BoolOpType::Union, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb3.arena); + out3.populate_vert(); + EXPECT_EQ(out3.vert_size(), 10); + EXPECT_EQ(out3.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out3, "tettet_union_binary_tm"); + } + + IMeshBuilder mb4(spec); + IMesh out4 = boolean_trimesh( + mb4.imesh, BoolOpType::Union, 2, [](int t) { return t < 4 ? 0 : 1; }, true, &mb4.arena); + out4.populate_vert(); + EXPECT_EQ(out4.vert_size(), 10); + EXPECT_EQ(out4.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out4, "tettet_union_binary_self_tm"); + } + + IMeshBuilder mb5(spec); + IMesh out5 = boolean_trimesh( + mb5.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb5.arena); + out5.populate_vert(); + EXPECT_EQ(out5.vert_size(), 4); + EXPECT_EQ(out5.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out5, "tettet_intersect_binary_tm"); + } + + IMeshBuilder mb6(spec); + IMesh out6 = boolean_trimesh( + mb6.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 4 ? 0 : 1; }, + false, + &mb6.arena); + out6.populate_vert(); + EXPECT_EQ(out6.vert_size(), 6); + EXPECT_EQ(out6.face_size(), 8); + if (DO_OBJ) { + write_obj_mesh(out6, "tettet_difference_binary_tm"); + } + + IMeshBuilder mb7(spec); + IMesh out7 = boolean_trimesh( + mb7.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 4 ? 1 : 0; }, + false, + &mb7.arena); + out7.populate_vert(); + EXPECT_EQ(out7.vert_size(), 8); + EXPECT_EQ(out7.face_size(), 12); + if (DO_OBJ) { + write_obj_mesh(out7, "tettet_difference_rev_binary_tm"); + } +} + +TEST(boolean_trimesh, TetTet2Trimesh) +{ + const char *spec = R"(8 8 + 0 1 -1 + 7/8 -1/2 -1 + -7/8 -1/2 -1 + 0 0 1 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 2 + 0 3 1 + 0 1 2 + 1 3 2 + 2 3 0 + 4 7 5 + 4 5 6 + 5 7 6 + 6 7 4 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 10); + EXPECT_EQ(out.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out, "tettet2_union_tm"); + } +} + +TEST(boolean_trimesh, CubeTetTrimesh) +{ + const char *spec = R"(12 16 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1/2 1/2 + 1/2 -1/4 1/2 + -1/2 -1/4 1/2 + 0 0 3/2 + 0 1 3 + 0 3 2 + 2 3 7 + 2 7 6 + 6 7 5 + 6 5 4 + 4 5 1 + 4 1 0 + 2 6 4 + 2 4 0 + 7 3 1 + 7 1 5 + 8 11 9 + 8 9 10 + 9 11 10 + 10 11 8 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 14); + EXPECT_EQ(out.face_size(), 24); + if (DO_OBJ) { + write_obj_mesh(out, "cubetet_union_tm"); + } +} + +TEST(boolean_trimesh, BinaryTetTetTrimesh) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh( + mb.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 4 ? 0 : 1; }, false, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 4); + EXPECT_EQ(out.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out, "binary_tettet_isect_tm"); + } +} + +TEST(boolean_trimesh, TetTetCoplanarTrimesh) +{ + const char *spec = R"(8 8 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 1 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 -1 + 0 3 1 + 0 1 2 + 1 3 2 + 2 3 0 + 4 5 7 + 4 6 5 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 5); + EXPECT_EQ(out.face_size(), 6); + if (DO_OBJ) { + write_obj_mesh(out, "tettet_coplanar_tm"); + } +} + +TEST(boolean_trimesh, TetInsideTetTrimesh) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + -1 -3/4 -1/2 + 3 -3/4 -1/2 + 1 13/4 -1/2 + 1 5/4 7/2 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 4); + EXPECT_EQ(out.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out, "tetinsidetet_tm"); + } +} + +TEST(boolean_trimesh, TetBesideTetTrimesh) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 3 0 0 + 5 0 0 + 4 2 0 + 4 1 2 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh(mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 8); + EXPECT_EQ(out.face_size(), 8); + if (DO_OBJ) { + write_obj_mesh(out, "tetbesidetet_tm"); + } +} + +TEST(boolean_trimesh, DegenerateTris) +{ + const char *spec = R"(10 10 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 0 0 + 1 0 0 + 0 2 1 + 0 8 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + 0 1 9 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_trimesh( + mb.imesh, BoolOpType::Intersect, 2, [](int t) { return t < 5 ? 0 : 1; }, false, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 4); + EXPECT_EQ(out.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out, "degenerate_tris_tm"); + } +} + +TEST(boolean_polymesh, TetTet) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 2 1 + 0 1 3 + 1 2 3 + 2 0 3 + 4 6 5 + 4 5 7 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, BoolOpType::None, 1, all_shape_zero, true, nullptr, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 11); + EXPECT_EQ(out.face_size(), 13); + if (DO_OBJ) { + write_obj_mesh(out, "tettet"); + } + + IMeshBuilder mb2(spec); + IMesh out2 = boolean_mesh( + mb2.imesh, + BoolOpType::None, + 2, + [](int t) { return t < 4 ? 0 : 1; }, + false, + nullptr, + &mb2.arena); + out2.populate_vert(); + EXPECT_EQ(out2.vert_size(), 11); + EXPECT_EQ(out2.face_size(), 13); + if (DO_OBJ) { + write_obj_mesh(out, "tettet2"); + } +} + +TEST(boolean_polymesh, CubeCube) +{ + const char *spec = R"(16 12 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 1/2 1/2 1/2 + 1/2 1/2 5/2 + 1/2 5/2 1/2 + 1/2 5/2 5/2 + 5/2 1/2 1/2 + 5/2 1/2 5/2 + 5/2 5/2 1/2 + 5/2 5/2 5/2 + 0 1 3 2 + 6 2 3 7 + 4 6 7 5 + 0 4 5 1 + 0 2 6 4 + 3 1 5 7 + 8 9 11 10 + 14 10 11 15 + 12 14 15 13 + 8 12 13 9 + 8 10 14 12 + 11 9 13 15 + )"; + + IMeshBuilder mb(spec); + if (DO_OBJ) { + write_obj_mesh(mb.imesh, "cube_cube_in"); + } + IMesh out = boolean_mesh( + mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, nullptr, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 20); + EXPECT_EQ(out.face_size(), 12); + if (DO_OBJ) { + write_obj_mesh(out, "cubecube_union"); + } + + IMeshBuilder mb2(spec); + IMesh out2 = boolean_mesh( + mb2.imesh, + BoolOpType::None, + 2, + [](int t) { return t < 6 ? 0 : 1; }, + false, + nullptr, + &mb2.arena); + out2.populate_vert(); + EXPECT_EQ(out2.vert_size(), 22); + EXPECT_EQ(out2.face_size(), 18); + if (DO_OBJ) { + write_obj_mesh(out2, "cubecube_none"); + } +} + +TEST(boolean_polymesh, CubeCone) +{ + const char *spec = R"(14 12 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1/2 3/4 + 119/250 31/200 3/4 + 147/500 -81/200 3/4 + 0 0 7/4 + -147/500 -81/200 3/4 + -119/250 31/200 3/4 + 0 1 3 2 + 2 3 7 6 + 6 7 5 4 + 4 5 1 0 + 2 6 4 0 + 7 3 1 5 + 8 11 9 + 9 11 10 + 10 11 12 + 12 11 13 + 13 11 8 + 8 9 10 12 13)"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, BoolOpType::Union, 1, all_shape_zero, true, nullptr, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 14); + EXPECT_EQ(out.face_size(), 12); + if (DO_OBJ) { + write_obj_mesh(out, "cubeccone"); + } +} + +TEST(boolean_polymesh, CubeCubeCoplanar) +{ + const char *spec = R"(16 12 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + -1/2 -1/2 1 + -1/2 -1/2 2 + -1/2 1/2 1 + -1/2 1/2 2 + 1/2 -1/2 1 + 1/2 -1/2 2 + 1/2 1/2 1 + 1/2 1/2 2 + 0 1 3 2 + 2 3 7 6 + 6 7 5 4 + 4 5 1 0 + 2 6 4 0 + 7 3 1 5 + 8 9 11 10 + 10 11 15 14 + 14 15 13 12 + 12 13 9 8 + 10 14 12 8 + 15 11 9 13 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Union, + 2, + [](int t) { return t < 6 ? 0 : 1; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 16); + EXPECT_EQ(out.face_size(), 12); + if (DO_OBJ) { + write_obj_mesh(out, "cubecube_coplanar"); + } +} + +TEST(boolean_polymesh, TetTeTCoplanarDiff) +{ + const char *spec = R"(8 8 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 1 + 0 1 0 + 7/8 -1/2 0 + -7/8 -1/2 0 + 0 0 -1 + 0 3 1 + 0 1 2 + 1 3 2 + 2 3 0 + 4 5 7 + 4 6 5 + 5 6 7 + 6 4 7 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 4 ? 0 : 1; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 4); + EXPECT_EQ(out.face_size(), 4); + if (DO_OBJ) { + write_obj_mesh(out, "tettet_coplanar_diff"); + } +} + +TEST(boolean_polymesh, CubeCubeStep) +{ + const char *spec = R"(16 12 + 0 -1 0 + 0 -1 2 + 0 1 0 + 0 1 2 + 2 -1 0 + 2 -1 2 + 2 1 0 + 2 1 2 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1 3 2 + 2 3 7 6 + 6 7 5 4 + 4 5 1 0 + 2 6 4 0 + 7 3 1 5 + 8 9 11 10 + 10 11 15 14 + 14 15 13 12 + 12 13 9 8 + 10 14 12 8 + 15 11 9 13 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 6 ? 0 : 1; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 12); + EXPECT_EQ(out.face_size(), 8); + if (DO_OBJ) { + write_obj_mesh(out, "cubecubestep"); + } +} + +TEST(boolean_polymesh, CubeCyl4) +{ + const char *spec = R"(16 12 + 0 1 -1 + 0 1 1 + 1 0 -1 + 1 0 1 + 0 -1 -1 + 0 -1 1 + -1 0 -1 + -1 0 1 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1 3 2 + 2 3 5 4 + 3 1 7 5 + 4 5 7 6 + 6 7 1 0 + 0 2 4 6 + 8 9 11 10 + 10 11 15 14 + 14 15 13 12 + 12 13 9 8 + 10 14 12 8 + 15 11 9 13 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 6 ? 1 : 0; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 16); + EXPECT_EQ(out.face_size(), 20); + if (DO_OBJ) { + write_obj_mesh(out, "cubecyl4"); + } +} + +TEST(boolean_polymesh, CubeCubesubdivDiff) +{ + /* A cube intersected by a subdivided cube that intersects first cubes edges exactly. */ + const char *spec = R"(26 22 + 2 1/3 2 + 2 -1/3 2 + 2 -1/3 0 + 2 1/3 0 + 0 -1/3 2 + 0 1/3 2 + 0 1/3 0 + 0 -1/3 0 + 1 1/3 2 + 1 -1/3 2 + 1 1/3 0 + 1 -1/3 0 + 0 -1/3 1 + 0 1/3 1 + 2 1/3 1 + 2 -1/3 1 + 1 1/3 1 + 1 -1/3 1 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 17 9 4 12 + 13 6 7 12 + 15 2 3 14 + 11 7 6 10 + 16 13 5 8 + 9 1 0 8 + 4 9 8 5 + 14 16 8 0 + 2 11 10 3 + 15 1 9 17 + 2 15 17 11 + 3 10 16 14 + 10 6 13 16 + 1 15 14 0 + 5 13 12 4 + 11 17 12 7 + 19 21 20 18 + 21 25 24 20 + 25 23 22 24 + 23 19 18 22 + 18 20 24 22 + 23 25 21 19 + )"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t < 16 ? 1 : 0; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 16); + EXPECT_EQ(out.face_size(), 10); + if (DO_OBJ) { + write_obj_mesh(out, "cubecubesubdivdiff"); + } +} + +TEST(boolean_polymesh, CubePlane) +{ + const char *spec = R"(12 7 + -2 -2 0 + 2 -2 0 + -2 2 0 + 2 2 0 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1 3 2 + 4 5 7 6 + 6 7 11 10 + 10 11 9 8 + 8 9 5 4 + 6 10 8 4 + 11 7 5 9 +)"; + + IMeshBuilder mb(spec); + IMesh out = boolean_mesh( + mb.imesh, + BoolOpType::Difference, + 2, + [](int t) { return t >= 1 ? 0 : 1; }, + false, + nullptr, + &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 8); + EXPECT_EQ(out.face_size(), 6); + if (DO_OBJ) { + write_obj_mesh(out, "cubeplane"); + } +} + +} // namespace blender::meshintersect::tests +#endif diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc new file mode 100644 index 00000000000..a9b916f6489 --- /dev/null +++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc @@ -0,0 +1,1074 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include <algorithm> +#include <fstream> +#include <iostream> + +#include "PIL_time.h" + +#include "BLI_array.hh" +#include "BLI_math_mpq.hh" +#include "BLI_mesh_intersect.hh" +#include "BLI_mpq3.hh" +#include "BLI_task.h" +#include "BLI_vector.hh" + +#define DO_REGULAR_TESTS 1 +#define DO_PERF_TESTS 0 + +#ifdef WITH_GMP +namespace blender::meshintersect::tests { + +constexpr bool DO_OBJ = false; + +/* Build and hold an IMesh from a string spec. Also hold and own resources used by IMesh. */ +class IMeshBuilder { + public: + IMesh imesh; + IMeshArena arena; + + /* "Edge orig" indices are an encoding of <input face#, position in face of start of edge>. */ + static constexpr int MAX_FACE_LEN = 1000; /* Used for forming "orig edge" indices only. */ + + static int edge_index(int face_index, int facepos) + { + return face_index * MAX_FACE_LEN + facepos; + } + + static std::pair<int, int> face_and_pos_for_edge_index(int e_index) + { + return std::pair<int, int>(e_index / MAX_FACE_LEN, e_index % MAX_FACE_LEN); + } + + /* + * Spec should have form: + * #verts #faces + * mpq_class mpq_class mpq_clas [#verts lines] + * int int int ... [#faces lines; indices into verts for given face] + */ + IMeshBuilder(const char *spec) + { + std::istringstream ss(spec); + std::string line; + getline(ss, line); + std::istringstream hdrss(line); + int nv, nf; + hdrss >> nv >> nf; + if (nv == 0 || nf == 0) { + return; + } + arena.reserve(nv, nf); + Vector<const Vert *> verts; + Vector<Face *> faces; + bool spec_ok = true; + int v_index = 0; + while (v_index < nv && spec_ok && getline(ss, line)) { + std::istringstream iss(line); + mpq_class p0; + mpq_class p1; + mpq_class p2; + iss >> p0 >> p1 >> p2; + spec_ok = !iss.fail(); + if (spec_ok) { + verts.append(arena.add_or_find_vert(mpq3(p0, p1, p2), v_index)); + } + ++v_index; + } + if (v_index != nv) { + spec_ok = false; + } + int f_index = 0; + while (f_index < nf && spec_ok && getline(ss, line)) { + std::istringstream fss(line); + Vector<const Vert *> face_verts; + Vector<int> edge_orig; + int fpos = 0; + while (spec_ok && fss >> v_index) { + if (v_index < 0 || v_index >= nv) { + spec_ok = false; + continue; + } + face_verts.append(verts[v_index]); + edge_orig.append(edge_index(f_index, fpos)); + ++fpos; + } + if (fpos < 3) { + spec_ok = false; + } + if (spec_ok) { + Face *facep = arena.add_face(face_verts, f_index, edge_orig); + faces.append(facep); + } + ++f_index; + } + if (f_index != nf) { + spec_ok = false; + } + if (!spec_ok) { + std::cout << "Bad spec: " << spec; + return; + } + imesh = IMesh(faces); + } +}; + +/* Return a const Face * in mesh with verts equal to v0, v1, and v2, in + * some cyclic order; return nullptr if not found. + */ +static const Face *find_tri_with_verts(const IMesh &mesh, + const Vert *v0, + const Vert *v1, + const Vert *v2) +{ + Face f_arg({v0, v1, v2}, 0, NO_INDEX); + for (const Face *f : mesh.faces()) { + if (f->cyclic_equal(f_arg)) { + return f; + } + } + return nullptr; +} + +/* How many instances of a triangle with v0, v1, v2 are in the mesh? */ +static int count_tris_with_verts(const IMesh &mesh, const Vert *v0, const Vert *v1, const Vert *v2) +{ + Face f_arg({v0, v1, v2}, 0, NO_INDEX); + int ans = 0; + for (const Face *f : mesh.faces()) { + if (f->cyclic_equal(f_arg)) { + ++ans; + } + } + return ans; +} + +/* What is the starting position, if any, of the edge (v0, v1), in either order, in f? -1 if none. + */ +static int find_edge_pos_in_tri(const Vert *v0, const Vert *v1, const Face *f) +{ + for (int pos : f->index_range()) { + int nextpos = f->next_pos(pos); + if (((*f)[pos] == v0 && (*f)[nextpos] == v1) || ((*f)[pos] == v1 && (*f)[nextpos] == v0)) { + return static_cast<int>(pos); + } + } + return -1; +} + +# if DO_REGULAR_TESTS +TEST(mesh_intersect, Mesh) +{ + Vector<const Vert *> verts; + Vector<Face *> faces; + IMeshArena arena; + + verts.append(arena.add_or_find_vert(mpq3(0, 0, 1), 0)); + verts.append(arena.add_or_find_vert(mpq3(1, 0, 1), 1)); + verts.append(arena.add_or_find_vert(mpq3(0.5, 1, 1), 2)); + faces.append(arena.add_face(verts, 0, {10, 11, 12})); + + IMesh mesh(faces); + const Face *f = mesh.face(0); + EXPECT_TRUE(f->is_tri()); +} + +TEST(mesh_intersect, OneTri) +{ + const char *spec = R"(3 1 + 0 0 0 + 1 0 0 + 1/2 1 0 + 0 1 2 + )"; + + IMeshBuilder mb(spec); + IMesh imesh = trimesh_self_intersect(mb.imesh, &mb.arena); + imesh.populate_vert(); + EXPECT_EQ(imesh.vert_size(), 3); + EXPECT_EQ(imesh.face_size(), 1); + const Face &f_in = *mb.imesh.face(0); + const Face &f_out = *imesh.face(0); + EXPECT_EQ(f_in.orig, f_out.orig); + for (int i = 0; i < 3; ++i) { + EXPECT_EQ(f_in[i], f_out[i]); + EXPECT_EQ(f_in.edge_orig[i], f_out.edge_orig[i]); + } +} + +TEST(mesh_intersect, TriTri) +{ + const char *spec = R"(6 2 + 0 0 0 + 4 0 0 + 0 4 0 + 1 0 0 + 2 0 0 + 1 1 0 + 0 1 2 + 3 4 5 + )"; + + /* Second triangle is smaller and congruent to first, resting on same base, partway along. */ + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 6); + EXPECT_EQ(out.face_size(), 6); + if (out.vert_size() == 6 && out.face_size() == 6) { + const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0)); + const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0)); + const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0)); + const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0)); + const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0)); + const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0)); + EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr); + EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr); + if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr && + v5 != nullptr) { + EXPECT_EQ(v0->orig, 0); + EXPECT_EQ(v1->orig, 1); + const Face *f0 = find_tri_with_verts(out, v4, v1, v5); + const Face *f1 = find_tri_with_verts(out, v3, v4, v5); + const Face *f2 = find_tri_with_verts(out, v0, v3, v5); + const Face *f3 = find_tri_with_verts(out, v0, v5, v2); + const Face *f4 = find_tri_with_verts(out, v5, v1, v2); + EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && + f4 != nullptr); + /* For boolean to work right, there need to be two copies of the smaller triangle in the + * output. */ + EXPECT_EQ(count_tris_with_verts(out, v3, v4, v5), 2); + if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) { + EXPECT_EQ(f0->orig, 0); + EXPECT_TRUE(f1->orig == 0 || f1->orig == 1); + EXPECT_EQ(f2->orig, 0); + EXPECT_EQ(f3->orig, 0); + EXPECT_EQ(f4->orig, 0); + } + int e03 = find_edge_pos_in_tri(v0, v3, f2); + int e34 = find_edge_pos_in_tri(v3, v4, f1); + int e45 = find_edge_pos_in_tri(v4, v5, f1); + int e05 = find_edge_pos_in_tri(v0, v5, f3); + int e15 = find_edge_pos_in_tri(v1, v5, f0); + EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1); + if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) { + EXPECT_EQ(f2->edge_orig[e03], 0); + EXPECT_TRUE(f1->edge_orig[e34] == 0 || + f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN); + EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1); + EXPECT_EQ(f3->edge_orig[e05], NO_INDEX); + EXPECT_EQ(f0->edge_orig[e15], NO_INDEX); + } + } + } + if (DO_OBJ) { + write_obj_mesh(out, "tritri"); + } +} + +TEST(mesh_intersect, TriTriReversed) +{ + /* Like TriTri but with triangles of opposite orientation. + * This matters because projection to 2D will now need reversed triangles. */ + const char *spec = R"(6 2 + 0 0 0 + 4 0 0 + 0 4 0 + 1 0 0 + 2 0 0 + 1 1 0 + 0 2 1 + 3 5 4 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 6); + EXPECT_EQ(out.face_size(), 6); + if (out.vert_size() == 6 && out.face_size() == 6) { + const Vert *v0 = mb.arena.find_vert(mpq3(0, 0, 0)); + const Vert *v1 = mb.arena.find_vert(mpq3(4, 0, 0)); + const Vert *v2 = mb.arena.find_vert(mpq3(0, 4, 0)); + const Vert *v3 = mb.arena.find_vert(mpq3(1, 0, 0)); + const Vert *v4 = mb.arena.find_vert(mpq3(2, 0, 0)); + const Vert *v5 = mb.arena.find_vert(mpq3(1, 1, 0)); + EXPECT_TRUE(v0 != nullptr && v1 != nullptr && v2 != nullptr); + EXPECT_TRUE(v3 != nullptr && v4 != nullptr && v5 != nullptr); + if (v0 != nullptr && v1 != nullptr && v2 != nullptr && v3 != nullptr && v4 != nullptr && + v5 != nullptr) { + EXPECT_EQ(v0->orig, 0); + EXPECT_EQ(v1->orig, 1); + const Face *f0 = find_tri_with_verts(out, v4, v5, v1); + const Face *f1 = find_tri_with_verts(out, v3, v5, v4); + const Face *f2 = find_tri_with_verts(out, v0, v5, v3); + const Face *f3 = find_tri_with_verts(out, v0, v2, v5); + const Face *f4 = find_tri_with_verts(out, v5, v2, v1); + EXPECT_TRUE(f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && + f4 != nullptr); + /* For boolean to work right, there need to be two copies of the smaller triangle in the + * output. */ + EXPECT_EQ(count_tris_with_verts(out, v3, v5, v4), 2); + if (f0 != nullptr && f1 != nullptr && f2 != nullptr && f3 != nullptr && f4 != nullptr) { + EXPECT_EQ(f0->orig, 0); + EXPECT_TRUE(f1->orig == 0 || f1->orig == 1); + EXPECT_EQ(f2->orig, 0); + EXPECT_EQ(f3->orig, 0); + EXPECT_EQ(f4->orig, 0); + } + int e03 = find_edge_pos_in_tri(v0, v3, f2); + int e34 = find_edge_pos_in_tri(v3, v4, f1); + int e45 = find_edge_pos_in_tri(v4, v5, f1); + int e05 = find_edge_pos_in_tri(v0, v5, f3); + int e15 = find_edge_pos_in_tri(v1, v5, f0); + EXPECT_TRUE(e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1); + if (e03 != -1 && e34 != -1 && e45 != -1 && e05 != -1 && e15 != -1) { + EXPECT_EQ(f2->edge_orig[e03], 2); + EXPECT_TRUE(f1->edge_orig[e34] == 2 || + f1->edge_orig[e34] == 1 * IMeshBuilder::MAX_FACE_LEN + 2); + EXPECT_EQ(f1->edge_orig[e45], 1 * IMeshBuilder::MAX_FACE_LEN + 1); + EXPECT_EQ(f3->edge_orig[e05], NO_INDEX); + EXPECT_EQ(f0->edge_orig[e15], NO_INDEX); + } + } + } + if (DO_OBJ) { + write_obj_mesh(out, "tritrirev"); + } +} + +TEST(mesh_intersect, TwoTris) +{ + Array<mpq3> verts = { + mpq3(1, 1, 1), mpq3(1, 4, 1), mpq3(1, 1, 4), /* T0 */ + mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(-4, 1, 3), /* T1 */ + mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(0, 3, 5), /* T2 */ + mpq3(2, 2, 2), mpq3(-3, 3, 2), mpq3(0, 3, 3), /* T3 */ + mpq3(1, 0, 0), mpq3(2, 4, 1), mpq3(-3, 2, 2), /* T4 */ + mpq3(0, 2, 1), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T5 */ + mpq3(1.5, 2, 0.5), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T6 */ + mpq3(1, 0, 0), mpq3(-2, 3, 3), mpq3(0, 1, 3), /* T7 */ + mpq3(1, 0, 0), mpq3(-3, 2, 2), mpq3(0, 1, 3), /* T8 */ + mpq3(1, 0, 0), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T9 */ + mpq3(3, -1, -1), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T10 */ + mpq3(0, 0.5, 0.5), mpq3(-1, 1, 1), mpq3(0, 1, 3), /* T11 */ + mpq3(2, 1, 1), mpq3(3, 5, 2), mpq3(-2, 3, 3), /* T12 */ + mpq3(2, 1, 1), mpq3(3, 5, 2), mpq3(-2, 3, 4), /* T13 */ + mpq3(2, 2, 5), mpq3(-3, 3, 5), mpq3(0, 3, 10), /* T14 */ + mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-4, 2, 4), /* T15 */ + mpq3(0, 1.5, 1), mpq3(1, 2.5, 1), mpq3(-1, 2, 2), /* T16 */ + mpq3(3, 0, -2), mpq3(7, 4, -2), mpq3(-1, 2, 2), /* T17 */ + mpq3(3, 0, -2), mpq3(3, 6, 2), mpq3(-1, 2, 2), /* T18 */ + mpq3(7, 4, -2), mpq3(3, 6, 2), mpq3(-1, 2, 2), /* T19 */ + mpq3(5, 2, -2), mpq3(1, 4, 2), mpq3(-3, 0, 2), /* T20 */ + mpq3(2, 2, 0), mpq3(1, 4, 2), mpq3(-3, 0, 2), /* T21 */ + mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-3, 0, 2), /* T22 */ + mpq3(0, 0, 0), mpq3(4, 4, 0), mpq3(-1, 2, 2), /* T23 */ + mpq3(2, 2, 0), mpq3(4, 4, 0), mpq3(0, 3, 2), /* T24 */ + mpq3(0, 0, 0), mpq3(-4, 2, 4), mpq3(4, 4, 0), /* T25 */ + }; + struct two_tri_test_spec { + int t0; + int t1; + int nv_out; + int nf_out; + }; + Array<two_tri_test_spec> test_tris = Span<two_tri_test_spec>{ + {0, 1, 8, 8}, /* 0: T1 pierces T0 inside at (1,11/6,13/6) and (1,11/5,2). */ + {0, 2, 8, 8}, /* 1: T2 intersects T0 inside (1,11/5,2) and edge (1,7/3,8/3). */ + {0, 3, 8, 7}, /* 2: T3 intersects T0 (1,11/5,2) and edge-edge (1,5/2,5/2). */ + {4, 5, 6, 4}, /* 3: T5 touches T4 inside (0,2,1). */ + {4, 6, 6, 3}, /* 4: T6 touches T4 on edge (3/2,2/1/2). */ + {4, 7, 5, 2}, /* 5: T7 touches T4 on vert (1,0,0). */ + {4, 8, 4, 2}, /* 6: T8 shared edge with T4 (1,0,0)(-3,2,2). */ + {4, 9, 5, 3}, /* 7: T9 edge (1,0,0)(-1,1,1) is subset of T4 edge. */ + {4, 10, 6, 4}, /* 8: T10 edge overlaps T4 edge with seg (-1,1,0)(1,0,0). */ + {4, 11, 6, 4}, /* 9: T11 edge (-1,1,1)(0,1/2,1/2) inside T4 edge. */ + {4, 12, 6, 2}, /* 10: parallel planes, not intersecting. */ + {4, 13, 6, 2}, /* 11: non-parallel planes, not intersecting, all one side. */ + {0, 14, 6, 2}, /* 12: non-paralel planes, not intersecting, alternate sides. */ + /* Following are all coplanar cases. */ + {15, 16, 6, 8}, /* 13: T16 inside T15. Note: dup'd tri is expected. */ + {15, 17, 8, 8}, /* 14: T17 intersects one edge of T15 at (1,1,0)(3,3,0). */ + {15, 18, 10, 12}, /* 15: T18 intersects T15 at (1,1,0)(3,3,0)(3,15/4,1/2)(0,3,2). */ + {15, 19, 8, 10}, /* 16: T19 intersects T15 at (3,3,0)(0,3,2). */ + {15, 20, 12, 14}, /* 17: T20 intersects T15 on three edges, six intersects. */ + {15, 21, 10, 11}, /* 18: T21 intersects T15 on three edges, touching one. */ + {15, 22, 5, 4}, /* 19: T22 shares edge T15, one other outside. */ + {15, 23, 4, 4}, /* 20: T23 shares edge T15, one other outside. */ + {15, 24, 5, 4}, /* 21: T24 shares two edges with T15. */ + {15, 25, 3, 2}, /* 22: T25 same T15, reverse orientation. */ + }; + static int perms[6][3] = {{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}; + + const int do_only_test = -1; /* Make this negative to do all tests. */ + for (int test = 0; test < test_tris.size(); ++test) { + if (do_only_test >= 0 && test != do_only_test) { + continue; + } + int tri1_index = test_tris[test].t0; + int tri2_index = test_tris[test].t1; + int co1_i = 3 * tri1_index; + int co2_i = 3 * tri2_index; + + const bool verbose = false; + + if (verbose) { + std::cout << "\nTest " << test << ": T" << tri1_index << " intersect T" << tri2_index + << "\n"; + } + + const bool do_all_perms = true; + const int perm_limit = do_all_perms ? 3 : 1; + + for (int i = 0; i < perm_limit; ++i) { + for (int j = 0; j < perm_limit; ++j) { + if (do_all_perms && verbose) { + std::cout << "\nperms " << i << " " << j << "\n"; + } + IMeshArena arena; + arena.reserve(2 * 3, 2); + Array<const Vert *> f0_verts(3); + Array<const Vert *> f1_verts(3); + for (int k = 0; k < 3; ++k) { + f0_verts[k] = arena.add_or_find_vert(verts[co1_i + perms[i][k]], k); + } + for (int k = 0; k < 3; ++k) { + f1_verts[k] = arena.add_or_find_vert(verts[co2_i + perms[i][k]], k + 3); + } + Face *f0 = arena.add_face(f0_verts, 0, {0, 1, 2}); + Face *f1 = arena.add_face(f1_verts, 1, {3, 4, 5}); + IMesh in_mesh({f0, f1}); + IMesh out_mesh = trimesh_self_intersect(in_mesh, &arena); + out_mesh.populate_vert(); + EXPECT_EQ(out_mesh.vert_size(), test_tris[test].nv_out); + EXPECT_EQ(out_mesh.face_size(), test_tris[test].nf_out); + bool constexpr dump_input = true; + if (DO_OBJ && i == 0 && j == 0) { + if (dump_input) { + std::string name = "test_tt_in" + std::to_string(test); + write_obj_mesh(in_mesh, name); + } + std::string name = "test_tt" + std::to_string(test); + write_obj_mesh(out_mesh, name); + } + } + } + } +} + +TEST(mesh_intersect, OverlapCluster) +{ + /* Chain of 5 overlapping coplanar tris. + * Ordered so that clustering will make two separate clusters + * that it will have to merge into one cluster with everything. */ + const char *spec = R"(15 5 + 0 0 0 + 1 0 0 + 1/2 1 0 + 1/2 0 0 + 3/2 0 0 + 1 1 0 + 1 0 0 + 2 0 0 + 3/2 1 0 + 3/2 0 0 + 5/2 0 0 + 2 1 0 + 2 0 0 + 3 0 0 + 5/2 1 0 + 0 1 2 + 3 4 5 + 9 10 11 + 12 13 14 + 6 7 8 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 16); + EXPECT_EQ(out.face_size(), 18); + if (DO_OBJ) { + write_obj_mesh(out, "overlapcluster"); + } +} + +TEST(mesh_intersect, TriCornerCross1) +{ + /* A corner formed by 3 tris, and a 4th crossing two of them. */ + const char *spec = R"(12 4 + 0 0 0 + 1 0 0 + 0 0 1 + 0 0 0 + 0 1 0 + 0 0 1 + 0 0 0 + 1 0 0 + 0 1 0 + 1 1 1/2 + 1 -2 1/2 + -2 1 1/2 + 0 1 2 + 3 4 5 + 6 7 8 + 9 10 11 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 10); + EXPECT_EQ(out.face_size(), 14); + if (DO_OBJ) { + write_obj_mesh(out, "test_tc_1"); + } +} + +TEST(mesh_intersect, TriCornerCross2) +{ + /* A corner formed by 3 tris, and a 4th coplanar with base. */ + const char *spec = R"(12 4 + 0 0 0 + 1 0 0 + 0 0 1 + 0 0 0 + 0 1 0 + 0 0 1 + 0 0 0 + 1 0 0 + 0 1 0 + 1 1 0 + 1 -2 0 + -2 1 0 + 0 1 2 + 3 4 5 + 6 7 8 + 9 10 11 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 7); + EXPECT_EQ(out.face_size(), 8); + if (DO_OBJ) { + write_obj_mesh(out, "test_tc_2"); + } +} + +TEST(mesh_intersect, TriCornerCross3) +{ + /* A corner formed by 3 tris, and a 4th crossing all 3. */ + const char *spec = R"(12 4 + 0 0 0 + 1 0 0 + 0 0 1 + 0 0 0 + 0 1 0 + 0 0 1 + 0 0 0 + 1 0 0 + 0 1 0 + 3/2 -1/2 -1/4 + -1/2 3/2 -1/4 + -1/2 -1/2 3/4 + 0 1 2 + 3 4 5 + 6 7 8 + 9 10 11 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 10); + EXPECT_EQ(out.face_size(), 16); + if (DO_OBJ) { + write_obj_mesh(out, "test_tc_3"); + } +} + +TEST(mesh_intersect, TetTet) +{ + const char *spec = R"(8 8 + 0 0 0 + 2 0 0 + 1 2 0 + 1 1 2 + 0 0 1 + 2 0 1 + 1 2 1 + 1 1 3 + 0 1 2 + 0 3 1 + 1 3 2 + 2 3 0 + 4 5 6 + 4 7 5 + 5 7 6 + 6 7 4 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 11); + EXPECT_EQ(out.face_size(), 20); + /* Expect there to be a triangle with these three verts, oriented this way, with original face 1. + */ + const Vert *v1 = mb.arena.find_vert(mpq3(2, 0, 0)); + const Vert *v8 = mb.arena.find_vert(mpq3(0.5, 0.5, 1)); + const Vert *v9 = mb.arena.find_vert(mpq3(1.5, 0.5, 1)); + EXPECT_TRUE(v1 != nullptr && v8 != nullptr && v9 != nullptr); + const Face *f = mb.arena.find_face({v1, v8, v9}); + EXPECT_NE(f, nullptr); + EXPECT_EQ(f->orig, 1); + int v1pos = f->vert[0] == v1 ? 0 : (f->vert[1] == v1 ? 1 : 2); + EXPECT_EQ(f->edge_orig[v1pos], NO_INDEX); + EXPECT_EQ(f->edge_orig[(v1pos + 1) % 3], NO_INDEX); + EXPECT_EQ(f->edge_orig[(v1pos + 2) % 3], 1001); + EXPECT_EQ(f->is_intersect[v1pos], false); + EXPECT_EQ(f->is_intersect[(v1pos + 1) % 3], true); + EXPECT_EQ(f->is_intersect[(v1pos + 2) % 3], false); + if (DO_OBJ) { + write_obj_mesh(out, "test_tc_3"); + } +} + +TEST(mesh_intersect, CubeCubeStep) +{ + const char *spec = R"(16 24 + 0 -1 0 + 0 -1 2 + 0 1 0 + 0 1 2 + 2 -1 0 + 2 -1 2 + 2 1 0 + 2 1 2 + -1 -1 -1 + -1 -1 1 + -1 1 -1 + -1 1 1 + 1 -1 -1 + 1 -1 1 + 1 1 -1 + 1 1 1 + 0 1 3 + 0 3 2 + 2 3 7 + 2 7 6 + 6 7 5 + 6 5 4 + 4 5 1 + 4 1 0 + 2 6 4 + 2 4 0 + 7 3 1 + 7 1 5 + 8 9 11 + 8 11 10 + 10 11 15 + 10 15 14 + 14 15 13 + 14 13 12 + 12 13 9 + 12 9 8 + 10 14 12 + 10 12 8 + 15 11 9 + 15 9 13 + )"; + + IMeshBuilder mb(spec); + IMesh out = trimesh_self_intersect(mb.imesh, &mb.arena); + out.populate_vert(); + EXPECT_EQ(out.vert_size(), 22); + EXPECT_EQ(out.face_size(), 56); + if (DO_OBJ) { + write_obj_mesh(out, "test_cubecubestep"); + } + + IMeshBuilder mb2(spec); + IMesh out2 = trimesh_nary_intersect( + mb2.imesh, 2, [](int t) { return t < 12 ? 0 : 1; }, false, &mb2.arena); + out2.populate_vert(); + EXPECT_EQ(out2.vert_size(), 22); + EXPECT_EQ(out2.face_size(), 56); + if (DO_OBJ) { + write_obj_mesh(out2, "test_cubecubestep_nary"); + } +} +# endif + +# if DO_PERF_TESTS + +static void get_sphere_params( + int nrings, int nsegs, bool triangulate, int *r_num_verts, int *r_num_faces) +{ + *r_num_verts = nsegs * (nrings - 1) + 2; + if (triangulate) { + *r_num_faces = 2 * nsegs + 2 * nsegs * (nrings - 2); + } + else { + *r_num_faces = nsegs * nrings; + } +} + +static void fill_sphere_data(int nrings, + int nsegs, + const double3 ¢er, + double radius, + bool triangulate, + MutableSpan<Face *> face, + int vid_start, + int fid_start, + IMeshArena *arena) +{ + int num_verts; + int num_faces; + get_sphere_params(nrings, nsegs, triangulate, &num_verts, &num_faces); + BLI_assert(num_faces == face.size()); + Array<const Vert *> vert(num_verts); + const bool nrings_even = (nrings % 2 == 0); + int half_nrings = nrings / 2; + const bool nsegs_even = (nsegs % 2) == 0; + const bool nsegs_four_divisible = (nsegs % 4 == 0); + int half_nsegs = nrings; + int quarter_nsegs = half_nsegs / 2; + double delta_phi = 2 * M_PI / nsegs; + double delta_theta = M_PI / nrings; + int fid = fid_start; + int vid = vid_start; + auto vert_index_fn = [nrings, num_verts](int seg, int ring) { + if (ring == 0) { /* Top vert. */ + return num_verts - 2; + } + if (ring == nrings) { /* Bottom vert. */ + return num_verts - 1; + } + return seg * (nrings - 1) + (ring - 1); + }; + auto face_index_fn = [nrings](int seg, int ring) { return seg * nrings + ring; }; + auto tri_index_fn = [nrings, nsegs](int seg, int ring, int tri) { + if (ring == 0) { + return seg; + } + if (ring < nrings - 1) { + return nsegs + 2 * (ring - 1) * nsegs + 2 * seg + tri; + } + return nsegs + 2 * (nrings - 2) * nsegs + seg; + }; + Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */ + /* + * (x, y , z) is given from inclination theta and azimuth phi, + * where 0 <= theta <= pi; 0 <= phi <= 2pi. + * x = radius * sin(theta) cos(phi) + * y = radius * sin(theta) sin(phi) + * z = radius * cos(theta) + */ + for (int s = 0; s < nsegs; ++s) { + double phi = s * delta_phi; + double sin_phi; + double cos_phi; + /* Avoid use of trig functions for pi/2 divisible angles. */ + if (s == 0) { + /* phi = 0. */ + sin_phi = 0.0; + cos_phi = 1.0; + } + else if (nsegs_even && s == half_nsegs) { + /* phi = pi. */ + sin_phi = 0.0; + cos_phi = -1.0; + } + else if (nsegs_four_divisible && s == quarter_nsegs) { + /* phi = pi/2. */ + sin_phi = 1.0; + cos_phi = 0.0; + } + else if (nsegs_four_divisible && s == 3 * quarter_nsegs) { + /* phi = 3pi/2. */ + sin_phi = -1.0; + cos_phi = 0.0; + } + else { + sin_phi = sin(phi); + cos_phi = cos(phi); + } + for (int r = 1; r < nrings; ++r) { + double theta = r * delta_theta; + double r_sin_theta; + double r_cos_theta; + if (nrings_even && r == half_nrings) { + /* theta = pi/2. */ + r_sin_theta = radius; + r_cos_theta = 0.0; + } + else { + r_sin_theta = radius * sin(theta); + r_cos_theta = radius * cos(theta); + } + double x = r_sin_theta * cos_phi + center[0]; + double y = r_sin_theta * sin_phi + center[1]; + double z = r_cos_theta + center[2]; + const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++); + vert[vert_index_fn(s, r)] = v; + } + } + const Vert *vtop = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] + radius), + vid++); + const Vert *vbot = arena->add_or_find_vert(mpq3(center[0], center[1], center[2] - radius), + vid++); + vert[vert_index_fn(0, 0)] = vtop; + vert[vert_index_fn(0, nrings)] = vbot; + for (int s = 0; s < nsegs; ++s) { + int snext = (s + 1) % nsegs; + for (int r = 0; r < nrings; ++r) { + int rnext = r + 1; + int i0 = vert_index_fn(s, r); + int i1 = vert_index_fn(s, rnext); + int i2 = vert_index_fn(snext, rnext); + int i3 = vert_index_fn(snext, r); + Face *f; + Face *f2 = nullptr; + if (r == 0) { + f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid); + } + else if (r == nrings - 1) { + f = arena->add_face({vert[i0], vert[i1], vert[i3]}, fid++, eid); + } + else { + if (triangulate) { + f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid); + f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid); + } + else { + f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid); + } + } + if (triangulate) { + int f_index = tri_index_fn(s, r, 0); + face[f_index] = f; + if (r != 0 && r != nrings - 1) { + int f_index2 = tri_index_fn(s, r, 1); + face[f_index2] = f2; + } + } + else { + int f_index = face_index_fn(s, r); + face[f_index] = f; + } + } + } +} + +static void spheresphere_test(int nrings, double y_offset, bool use_self) +{ + /* Make two uvspheres with nrings rings ad 2*nrings segments. */ + if (nrings < 2) { + return; + } + BLI_task_scheduler_init(); /* Without this, no parallelism. */ + double time_start = PIL_check_seconds_timer(); + IMeshArena arena; + int nsegs = 2 * nrings; + int num_sphere_verts; + int num_sphere_tris; + get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris); + Array<Face *> tris(2 * num_sphere_tris); + arena.reserve(2 * num_sphere_verts, 2 * num_sphere_tris); + double3 center1(0.0, 0.0, 0.0); + fill_sphere_data(nrings, + nsegs, + center1, + 1.0, + true, + MutableSpan<Face *>(tris.begin(), num_sphere_tris), + 0, + 0, + &arena); + double3 center2(0.0, y_offset, 0.0); + fill_sphere_data(nrings, + nsegs, + center2, + 1.0, + true, + MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_sphere_tris), + num_sphere_verts, + num_sphere_verts, + &arena); + IMesh mesh(tris); + double time_create = PIL_check_seconds_timer(); + // write_obj_mesh(mesh, "spheresphere_in"); + IMesh out; + if (use_self) { + out = trimesh_self_intersect(mesh, &arena); + } + else { + int nf = num_sphere_tris; + out = trimesh_nary_intersect( + mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena); + } + double time_intersect = PIL_check_seconds_timer(); + std::cout << "Create time: " << time_create - time_start << "\n"; + std::cout << "Intersect time: " << time_intersect - time_create << "\n"; + std::cout << "Total time: " << time_intersect - time_start << "\n"; + if (DO_OBJ) { + write_obj_mesh(out, "spheresphere"); + } + BLI_task_scheduler_exit(); +} + +static void get_grid_params( + int x_subdiv, int y_subdiv, bool triangulate, int *r_num_verts, int *r_num_faces) +{ + *r_num_verts = x_subdiv * y_subdiv; + if (triangulate) { + *r_num_faces = 2 * (x_subdiv - 1) * (y_subdiv - 1); + } + else { + *r_num_faces = (x_subdiv - 1) * (y_subdiv - 1); + } +} + +static void fill_grid_data(int x_subdiv, + int y_subdiv, + bool triangulate, + double size, + const double3 ¢er, + MutableSpan<Face *> face, + int vid_start, + int fid_start, + IMeshArena *arena) +{ + if (x_subdiv <= 1 || y_subdiv <= 1) { + return; + } + int num_verts; + int num_faces; + get_grid_params(x_subdiv, y_subdiv, triangulate, &num_verts, &num_faces); + BLI_assert(face.size() == num_faces); + Array<const Vert *> vert(num_verts); + auto vert_index_fn = [x_subdiv](int ix, int iy) { return iy * x_subdiv + ix; }; + auto face_index_fn = [x_subdiv](int ix, int iy) { return iy * (x_subdiv - 1) + ix; }; + auto tri_index_fn = [x_subdiv](int ix, int iy, int tri) { + return 2 * iy * (x_subdiv - 1) + 2 * ix + tri; + }; + Array<int> eid = {0, 0, 0, 0}; /* Don't care about edge ids. */ + double r = size / 2.0; + double delta_x = size / (x_subdiv - 1); + double delta_y = size / (y_subdiv - 1); + int vid = vid_start; + for (int iy = 0; iy < y_subdiv; ++iy) { + for (int ix = 0; ix < x_subdiv; ++ix) { + double x = center[0] - r + ix * delta_x; + double y = center[1] - r + iy * delta_y; + double z = center[2]; + const Vert *v = arena->add_or_find_vert(mpq3(x, y, z), vid++); + vert[vert_index_fn(ix, iy)] = v; + } + } + int fid = fid_start; + for (int iy = 0; iy < y_subdiv - 1; ++iy) { + for (int ix = 0; ix < x_subdiv - 1; ++ix) { + int i0 = vert_index_fn(ix, iy); + int i1 = vert_index_fn(ix, iy + 1); + int i2 = vert_index_fn(ix + 1, iy + 1); + int i3 = vert_index_fn(ix + 1, iy); + if (triangulate) { + Face *f = arena->add_face({vert[i0], vert[i1], vert[i2]}, fid++, eid); + Face *f2 = arena->add_face({vert[i2], vert[i3], vert[i0]}, fid++, eid); + face[tri_index_fn(ix, iy, 0)] = f; + face[tri_index_fn(ix, iy, 1)] = f2; + } + else { + Face *f = arena->add_face({vert[i0], vert[i1], vert[i2], vert[i3]}, fid++, eid); + face[face_index_fn(ix, iy)] = f; + } + } + } +} + +static void spheregrid_test(int nrings, int grid_level, double z_offset, bool use_self) +{ + /* Make a uvsphere and a grid. + * The sphere is radius 1, has nrings rings and 2 * nrings segs, + * and is centered at (0,0,z_offset). + * The plane is 4x4, has 2**grid_level subdivisions x and y, + * and is centered at the origin. */ + if (nrings < 2 || grid_level < 1) { + return; + } + BLI_task_scheduler_init(); /* Without this, no parallelism. */ + double time_start = PIL_check_seconds_timer(); + IMeshArena arena; + int num_sphere_verts; + int num_sphere_tris; + int nsegs = 2 * nrings; + int num_grid_verts; + int num_grid_tris; + int subdivs = 1 << grid_level; + get_sphere_params(nrings, nsegs, true, &num_sphere_verts, &num_sphere_tris); + get_grid_params(subdivs, subdivs, true, &num_grid_verts, &num_grid_tris); + Array<Face *> tris(num_sphere_tris + num_grid_tris); + arena.reserve(num_sphere_verts + num_grid_verts, num_sphere_tris + num_grid_tris); + double3 center(0.0, 0.0, z_offset); + fill_sphere_data(nrings, + nsegs, + center, + 1.0, + true, + MutableSpan<Face *>(tris.begin(), num_sphere_tris), + 0, + 0, + &arena); + fill_grid_data(subdivs, + subdivs, + true, + 4.0, + double3(0, 0, 0), + MutableSpan<Face *>(tris.begin() + num_sphere_tris, num_grid_tris), + num_sphere_verts, + num_sphere_tris, + &arena); + IMesh mesh(tris); + double time_create = PIL_check_seconds_timer(); + // write_obj_mesh(mesh, "spheregrid_in"); + IMesh out; + if (use_self) { + out = trimesh_self_intersect(mesh, &arena); + } + else { + int nf = num_sphere_tris; + out = trimesh_nary_intersect( + mesh, 2, [nf](int t) { return t < nf ? 0 : 1; }, false, &arena); + } + double time_intersect = PIL_check_seconds_timer(); + std::cout << "Create time: " << time_create - time_start << "\n"; + std::cout << "Intersect time: " << time_intersect - time_create << "\n"; + std::cout << "Total time: " << time_intersect - time_start << "\n"; + if (DO_OBJ) { + write_obj_mesh(out, "spheregrid"); + } + BLI_task_scheduler_exit(); +} + +TEST(mesh_intersect_perf, SphereSphere) +{ + spheresphere_test(64, 0.5, false); +} + +TEST(mesh_intersect_perf, SphereGrid) +{ + spheregrid_test(64, 4, 0.1, false); +} + +# endif + +} // namespace blender::meshintersect::tests +#endif diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc index df3f7ab544c..3ea9a59b3db 100644 --- a/source/blender/blenlib/tests/BLI_set_test.cc +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -3,6 +3,7 @@ #include <set> #include <unordered_set> +#include "BLI_exception_safety_test_utils.hh" #include "BLI_ghash.h" #include "BLI_rand.h" #include "BLI_set.hh" @@ -462,6 +463,55 @@ TEST(set, StringViewKeys) EXPECT_TRUE(set.contains("hello")); } +TEST(set, SpanConstructorExceptions) +{ + std::array<ExceptionThrower, 5> array = {1, 2, 3, 4, 5}; + array[3].throw_during_copy = true; + Span<ExceptionThrower> span = array; + + EXPECT_ANY_THROW({ Set<ExceptionThrower> set(span); }); +} + +TEST(set, CopyConstructorExceptions) +{ + Set<ExceptionThrower> set = {1, 2, 3, 4, 5}; + set.lookup_key(3).throw_during_copy = true; + EXPECT_ANY_THROW({ Set<ExceptionThrower> set_copy(set); }); +} + +TEST(set, MoveConstructorExceptions) +{ + using SetType = Set<ExceptionThrower, 4>; + SetType set = {1, 2, 3}; + set.lookup_key(2).throw_during_move = true; + EXPECT_ANY_THROW({ SetType set_moved(std::move(set)); }); + EXPECT_EQ(set.size(), 0); + set.add_multiple({3, 6, 7}); + EXPECT_EQ(set.size(), 3); +} + +TEST(set, AddNewExceptions) +{ + Set<ExceptionThrower> set; + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ set.add_new(value); }); + EXPECT_EQ(set.size(), 0); + EXPECT_ANY_THROW({ set.add_new(value); }); + EXPECT_EQ(set.size(), 0); +} + +TEST(set, AddExceptions) +{ + Set<ExceptionThrower> set; + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ set.add(value); }); + EXPECT_EQ(set.size(), 0); + EXPECT_ANY_THROW({ set.add(value); }); + EXPECT_EQ(set.size(), 0); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc index 3572e751b88..c03893c5596 100644 --- a/source/blender/blenlib/tests/BLI_stack_cxx_test.cc +++ b/source/blender/blenlib/tests/BLI_stack_cxx_test.cc @@ -1,5 +1,6 @@ /* Apache License, Version 2.0 */ +#include "BLI_exception_safety_test_utils.hh" #include "BLI_stack.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" @@ -185,4 +186,59 @@ TEST(stack, OveralignedValues) } } +TEST(stack, SpanConstructorExceptions) +{ + std::array<ExceptionThrower, 5> values; + values[3].throw_during_copy = true; + EXPECT_ANY_THROW({ Stack<ExceptionThrower> stack(values); }); +} + +TEST(stack, MoveConstructorExceptions) +{ + Stack<ExceptionThrower, 4> stack; + stack.push({}); + stack.push({}); + stack.peek().throw_during_move = true; + EXPECT_ANY_THROW({ Stack<ExceptionThrower> moved_stack{std::move(stack)}; }); +} + +TEST(stack, PushExceptions) +{ + Stack<ExceptionThrower, 2> stack; + stack.push({}); + stack.push({}); + ExceptionThrower *ptr1 = &stack.peek(); + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ stack.push(value); }); + EXPECT_EQ(stack.size(), 2); + ExceptionThrower *ptr2 = &stack.peek(); + EXPECT_EQ(ptr1, ptr2); + EXPECT_TRUE(stack.is_invariant_maintained()); +} + +TEST(stack, PopExceptions) +{ + Stack<ExceptionThrower> stack; + stack.push({}); + stack.peek().throw_during_move = true; + stack.push({}); + stack.pop(); /* NOLINT: bugprone-throw-keyword-missing */ + EXPECT_ANY_THROW({ stack.pop(); }); /* NOLINT: bugprone-throw-keyword-missing */ + EXPECT_EQ(stack.size(), 1); + EXPECT_TRUE(stack.is_invariant_maintained()); +} + +TEST(stack, PushMultipleExceptions) +{ + Stack<ExceptionThrower> stack; + stack.push({}); + std::array<ExceptionThrower, 100> values; + values[6].throw_during_copy = true; + EXPECT_ANY_THROW({ stack.push_multiple(values); }); + EXPECT_TRUE(stack.is_invariant_maintained()); + EXPECT_ANY_THROW({ stack.push_multiple(values); }); + EXPECT_TRUE(stack.is_invariant_maintained()); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_vector_test.cc b/source/blender/blenlib/tests/BLI_vector_test.cc index 792e120d2c0..e6b2e7c6365 100644 --- a/source/blender/blenlib/tests/BLI_vector_test.cc +++ b/source/blender/blenlib/tests/BLI_vector_test.cc @@ -1,5 +1,6 @@ /* Apache License, Version 2.0 */ +#include "BLI_exception_safety_test_utils.hh" #include "BLI_strict_flags.h" #include "BLI_vector.hh" #include "testing/testing.h" @@ -709,4 +710,119 @@ TEST(vector, ReverseIterator) EXPECT_EQ_ARRAY(reversed_vec.data(), Span({7, 6, 5, 4}).data(), 4); } +TEST(vector, SizeValueConstructorExceptions) +{ + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ Vector<ExceptionThrower> vec(5, value); }); +} + +TEST(vector, SpanConstructorExceptions) +{ + std::array<ExceptionThrower, 5> values; + values[3].throw_during_copy = true; + EXPECT_ANY_THROW({ Vector<ExceptionThrower> vec(values); }); +} + +TEST(vector, MoveConstructorExceptions) +{ + Vector<ExceptionThrower, 4> vec(3); + vec[2].throw_during_move = true; + EXPECT_ANY_THROW({ Vector<ExceptionThrower> moved_vector{std::move(vec)}; }); +} + +TEST(vector, AppendExceptions) +{ + Vector<ExceptionThrower, 4> vec(2); + ExceptionThrower *ptr1 = &vec.last(); + ExceptionThrower value; + value.throw_during_copy = true; + EXPECT_ANY_THROW({ vec.append(value); }); + EXPECT_EQ(vec.size(), 2); + ExceptionThrower *ptr2 = &vec.last(); + EXPECT_EQ(ptr1, ptr2); +} + +TEST(vector, ExtendExceptions) +{ + Vector<ExceptionThrower> vec(5); + std::array<ExceptionThrower, 10> values; + values[6].throw_during_copy = true; + EXPECT_ANY_THROW({ vec.extend(values); }); + EXPECT_EQ(vec.size(), 5); +} + +TEST(vector, Insert1Exceptions) +{ + Vector<ExceptionThrower> vec(10); + std::array<ExceptionThrower, 5> values; + values[3].throw_during_copy = true; + EXPECT_ANY_THROW({ vec.insert(7, values); }); +} + +TEST(vector, Insert2Exceptions) +{ + Vector<ExceptionThrower> vec(10); + vec.reserve(100); + vec[8].throw_during_move = true; + std::array<ExceptionThrower, 5> values; + EXPECT_ANY_THROW({ vec.insert(3, values); }); +} + +TEST(vector, PopLastExceptions) +{ + Vector<ExceptionThrower> vec(10); + vec.last().throw_during_move = true; + EXPECT_ANY_THROW({ vec.pop_last(); }); /* NOLINT: bugprone-throw-keyword-missing */ + EXPECT_EQ(vec.size(), 10); +} + +TEST(vector, RemoveAndReorderExceptions) +{ + Vector<ExceptionThrower> vec(10); + vec.last().throw_during_move = true; + EXPECT_ANY_THROW({ vec.remove_and_reorder(3); }); + EXPECT_EQ(vec.size(), 10); +} + +TEST(vector, RemoveExceptions) +{ + Vector<ExceptionThrower> vec(10); + vec[8].throw_during_move = true; + EXPECT_ANY_THROW({ vec.remove(2); }); + EXPECT_EQ(vec.size(), 10); +} + +TEST(vector, RemoveChunk) +{ + Vector<int> vec = {2, 3, 4, 5, 6, 7, 8}; + EXPECT_EQ(vec.size(), 7); + vec.remove(2, 4); + EXPECT_EQ(vec.size(), 3); + EXPECT_EQ(vec[0], 2); + EXPECT_EQ(vec[1], 3); + EXPECT_EQ(vec[2], 8); + vec.remove(0, 1); + EXPECT_EQ(vec.size(), 2); + EXPECT_EQ(vec[0], 3); + EXPECT_EQ(vec[1], 8); + vec.remove(1, 1); + EXPECT_EQ(vec.size(), 1); + EXPECT_EQ(vec[0], 3); + vec.remove(0, 1); + EXPECT_EQ(vec.size(), 0); + vec.remove(0, 0); + EXPECT_EQ(vec.size(), 0); +} + +TEST(vector, RemoveChunkExceptions) +{ + Vector<ExceptionThrower> vec(10); + vec.remove(1, 3); + EXPECT_EQ(vec.size(), 7); + vec[5].throw_during_move = true; + EXPECT_ANY_THROW({ vec.remove(2, 3); }); + EXPECT_EQ(vec.size(), 7); +} + } // namespace blender::tests diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index 024b6a6c5e4..c2f3615725e 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -177,9 +177,12 @@ bool BLO_write_is_undo(BlendWriter *writer); */ void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_address); +void *BLO_read_get_new_packed_address(BlendDataReader *reader, const void *old_address); #define BLO_read_data_address(reader, ptr_p) \ *((void **)ptr_p) = BLO_read_get_new_data_address((reader), *(ptr_p)) +#define BLO_read_packed_address(reader, ptr_p) \ + *((void **)ptr_p) = BLO_read_get_new_packed_address((reader), *(ptr_p)) typedef void (*BlendReadListFn)(BlendDataReader *reader, void *data); void BLO_read_list_cb(BlendDataReader *reader, struct ListBase *list, BlendReadListFn callback); @@ -195,6 +198,7 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p); /* Misc. */ bool BLO_read_requires_endian_switch(BlendDataReader *reader); +bool BLO_read_data_is_undo(BlendDataReader *reader); /* Blend Read Lib API * =================== @@ -208,6 +212,9 @@ ID *BLO_read_get_new_id_address(BlendLibReader *reader, struct Library *lib, str #define BLO_read_id_address(reader, lib, id_ptr_p) \ *(id_ptr_p) = (void *)BLO_read_get_new_id_address((reader), (lib), (ID *)*(id_ptr_p)) +/* Misc. */ +bool BLO_read_lib_is_undo(BlendLibReader *reader); + /* Blend Expand API * =================== * diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 9003bc4f794..b4b25815d39 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -120,7 +120,9 @@ #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_curveprofile.h" +#include "BKE_deform.h" #include "BKE_effect.h" +#include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" #include "BKE_fluid.h" #include "BKE_global.h" // for G @@ -140,6 +142,7 @@ #include "BKE_mesh_runtime.h" #include "BKE_modifier.h" #include "BKE_multires.h" +#include "BKE_nla.h" #include "BKE_node.h" // for tree type defines #include "BKE_object.h" #include "BKE_paint.h" @@ -264,8 +267,6 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname); #ifdef USE_COLLECTION_COMPAT_28 static void expand_scene_collection(BlendExpander *expander, SceneCollection *sc); #endif -static void direct_link_animdata(BlendDataReader *reader, AnimData *adt); -static void lib_link_animdata(BlendLibReader *reader, ID *id, AnimData *adt); typedef struct BHeadN { struct BHeadN *next, *prev; @@ -1827,9 +1828,7 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, void *old, void *new) { - Main *mainptr; - - for (mainptr = mainlist->first; mainptr; mainptr = mainptr->next) { + LISTBASE_FOREACH (Main *, mainptr, mainlist) { FileData *fd; if (mainptr->curlib) { @@ -1853,9 +1852,7 @@ static void change_link_placeholder_to_real_ID_pointer(ListBase *mainlist, */ void blo_clear_proxy_pointers_from_lib(Main *oldmain) { - Object *ob = oldmain->objects.first; - - for (; ob; ob = ob->id.next) { + LISTBASE_FOREACH (Object *, ob, &oldmain->objects) { if (ob->id.lib != NULL && ob->proxy_from != NULL && ob->proxy_from->id.lib == NULL) { ob->proxy_from = NULL; } @@ -1873,47 +1870,39 @@ static void insert_packedmap(FileData *fd, PackedFile *pf) void blo_make_packed_pointer_map(FileData *fd, Main *oldmain) { - Image *ima; - VFont *vfont; - bSound *sound; - Volume *volume; - Library *lib; - fd->packedmap = oldnewmap_new(); - for (ima = oldmain->images.first; ima; ima = ima->id.next) { - ImagePackedFile *imapf; - + LISTBASE_FOREACH (Image *, ima, &oldmain->images) { if (ima->packedfile) { insert_packedmap(fd, ima->packedfile); } - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { if (imapf->packedfile) { insert_packedmap(fd, imapf->packedfile); } } } - for (vfont = oldmain->fonts.first; vfont; vfont = vfont->id.next) { + LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) { if (vfont->packedfile) { insert_packedmap(fd, vfont->packedfile); } } - for (sound = oldmain->sounds.first; sound; sound = sound->id.next) { + LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) { if (sound->packedfile) { insert_packedmap(fd, sound->packedfile); } } - for (volume = oldmain->volumes.first; volume; volume = volume->id.next) { + LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) { if (volume->packedfile) { insert_packedmap(fd, volume->packedfile); } } - for (lib = oldmain->libraries.first; lib; lib = lib->id.next) { + LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) { if (lib->packedfile) { insert_packedmap(fd, lib->packedfile); } @@ -1924,44 +1913,36 @@ void blo_make_packed_pointer_map(FileData *fd, Main *oldmain) /* this works because freeing old main only happens after this call */ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) { - Image *ima; - VFont *vfont; - bSound *sound; - Volume *volume; - Library *lib; OldNew *entry = fd->packedmap->entries; - int i; /* used entries were restored, so we put them to zero */ - for (i = 0; i < fd->packedmap->nentries; i++, entry++) { + for (int i = 0; i < fd->packedmap->nentries; i++, entry++) { if (entry->nr > 0) { entry->newp = NULL; } } - for (ima = oldmain->images.first; ima; ima = ima->id.next) { - ImagePackedFile *imapf; - + LISTBASE_FOREACH (Image *, ima, &oldmain->images) { ima->packedfile = newpackedadr(fd, ima->packedfile); - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { imapf->packedfile = newpackedadr(fd, imapf->packedfile); } } - for (vfont = oldmain->fonts.first; vfont; vfont = vfont->id.next) { + LISTBASE_FOREACH (VFont *, vfont, &oldmain->fonts) { vfont->packedfile = newpackedadr(fd, vfont->packedfile); } - for (sound = oldmain->sounds.first; sound; sound = sound->id.next) { + LISTBASE_FOREACH (bSound *, sound, &oldmain->sounds) { sound->packedfile = newpackedadr(fd, sound->packedfile); } - for (lib = oldmain->libraries.first; lib; lib = lib->id.next) { + LISTBASE_FOREACH (Library *, lib, &oldmain->libraries) { lib->packedfile = newpackedadr(fd, lib->packedfile); } - for (volume = oldmain->volumes.first; volume; volume = volume->id.next) { + LISTBASE_FOREACH (Volume *, volume, &oldmain->volumes) { volume->packedfile = newpackedadr(fd, volume->packedfile); } } @@ -1969,14 +1950,12 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) /* undo file support: add all library pointers in lookup */ void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd) { - Main *ptr = old_mainlist->first; ListBase *lbarray[MAX_LIBARRAY]; - for (ptr = ptr->next; ptr; ptr = ptr->next) { + LISTBASE_FOREACH (Main *, ptr, old_mainlist) { int i = set_listbasepointers(ptr, lbarray); while (i--) { - ID *id; - for (id = lbarray[i]->first; id; id = id->next) { + LISTBASE_FOREACH (ID *, id, lbarray[i]) { oldnewmap_insert(fd->libmap, id, id, GS(id->name)); } } @@ -2004,7 +1983,7 @@ typedef struct BLOCacheStorage { static void blo_cache_storage_entry_register(ID *id, const IDCacheKey *key, void **UNUSED(cache_p), - eIDTypeInfoCacheCallbackFlags UNUSED(flags), + uint UNUSED(flags), void *cache_storage_v) { BLI_assert(key->id_session_uuid == id->session_uuid); @@ -2019,11 +1998,8 @@ static void blo_cache_storage_entry_register(ID *id, } /** Restore a cache data entry from old ID into new one, when reading some undo memfile. */ -static void blo_cache_storage_entry_restore_in_new(ID *UNUSED(id), - const IDCacheKey *key, - void **cache_p, - eIDTypeInfoCacheCallbackFlags flags, - void *cache_storage_v) +static void blo_cache_storage_entry_restore_in_new( + ID *UNUSED(id), const IDCacheKey *key, void **cache_p, uint flags, void *cache_storage_v) { BLOCacheStorage *cache_storage = cache_storage_v; @@ -2050,7 +2026,7 @@ static void blo_cache_storage_entry_restore_in_new(ID *UNUSED(id), static void blo_cache_storage_entry_clear_in_old(ID *UNUSED(id), const IDCacheKey *key, void **cache_p, - eIDTypeInfoCacheCallbackFlags UNUSED(flags), + uint UNUSED(flags), void *cache_storage_v) { BLOCacheStorage *cache_storage = cache_storage_v; @@ -2270,185 +2246,6 @@ static void link_glob_list(FileData *fd, ListBase *lb) /* for glob data */ /** \} */ /* -------------------------------------------------------------------- */ -/** \name Read ID Properties - * \{ */ - -static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader); -static void IDP_LibLinkProperty(IDProperty *prop, BlendLibReader *reader); - -static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader) -{ - IDProperty *array; - int i; - - /* since we didn't save the extra buffer, set totallen to len */ - prop->totallen = prop->len; - BLO_read_data_address(reader, &prop->data.pointer); - - array = (IDProperty *)prop->data.pointer; - - /* note!, idp-arrays didn't exist in 2.4x, so the pointer will be cleared - * there's not really anything we can do to correct this, at least don't crash */ - if (array == NULL) { - prop->len = 0; - prop->totallen = 0; - } - - for (i = 0; i < prop->len; i++) { - IDP_DirectLinkProperty(&array[i], reader); - } -} - -static void IDP_DirectLinkArray(IDProperty *prop, BlendDataReader *reader) -{ - IDProperty **array; - int i; - - /* since we didn't save the extra buffer, set totallen to len */ - prop->totallen = prop->len; - - if (prop->subtype == IDP_GROUP) { - BLO_read_pointer_array(reader, &prop->data.pointer); - array = prop->data.pointer; - - for (i = 0; i < prop->len; i++) { - IDP_DirectLinkProperty(array[i], reader); - } - } - else if (prop->subtype == IDP_DOUBLE) { - BLO_read_double_array(reader, prop->len, (double **)&prop->data.pointer); - } - else { - /* also used for floats */ - BLO_read_int32_array(reader, prop->len, (int **)&prop->data.pointer); - } -} - -static void IDP_DirectLinkString(IDProperty *prop, BlendDataReader *reader) -{ - /*since we didn't save the extra string buffer, set totallen to len.*/ - prop->totallen = prop->len; - BLO_read_data_address(reader, &prop->data.pointer); -} - -static void IDP_DirectLinkGroup(IDProperty *prop, BlendDataReader *reader) -{ - ListBase *lb = &prop->data.group; - IDProperty *loop; - - BLO_read_list(reader, lb); - - /*Link child id properties now*/ - for (loop = prop->data.group.first; loop; loop = loop->next) { - IDP_DirectLinkProperty(loop, reader); - } -} - -static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader) -{ - switch (prop->type) { - case IDP_GROUP: - IDP_DirectLinkGroup(prop, reader); - break; - case IDP_STRING: - IDP_DirectLinkString(prop, reader); - break; - case IDP_ARRAY: - IDP_DirectLinkArray(prop, reader); - break; - case IDP_IDPARRAY: - IDP_DirectLinkIDPArray(prop, reader); - break; - case IDP_DOUBLE: - /* Workaround for doubles. - * They are stored in the same field as `int val, val2` in the IDPropertyData struct, - * they have to deal with endianness specifically. - * - * In theory, val and val2 would've already been swapped - * if switch_endian is true, so we have to first unswap - * them then re-swap them as a single 64-bit entity. */ - if (BLO_read_requires_endian_switch(reader)) { - BLI_endian_switch_int32(&prop->data.val); - BLI_endian_switch_int32(&prop->data.val2); - BLI_endian_switch_int64((int64_t *)&prop->data.val); - } - break; - case IDP_INT: - case IDP_FLOAT: - case IDP_ID: - break; /* Nothing special to do here. */ - default: - /* Unknown IDP type, nuke it (we cannot handle unknown types everywhere in code, - * IDP are way too polymorphic to do it safely. */ - printf( - "%s: found unknown IDProperty type %d, reset to Integer one !\n", __func__, prop->type); - /* Note: we do not attempt to free unknown prop, we have no way to know how to do that! */ - prop->type = IDP_INT; - prop->subtype = 0; - IDP_Int(prop) = 0; - } -} - -#define IDP_DirectLinkGroup_OrFree(prop, reader) \ - _IDP_DirectLinkGroup_OrFree(prop, reader, __func__) - -static void _IDP_DirectLinkGroup_OrFree(IDProperty **prop, - BlendDataReader *reader, - const char *caller_func_id) -{ - if (*prop) { - if ((*prop)->type == IDP_GROUP) { - IDP_DirectLinkGroup(*prop, reader); - } - else { - /* corrupt file! */ - printf("%s: found non group data, freeing type %d!\n", caller_func_id, (*prop)->type); - /* don't risk id, data's likely corrupt. */ - // IDP_FreePropertyContent(*prop); - *prop = NULL; - } - } -} - -static void IDP_LibLinkProperty(IDProperty *prop, BlendLibReader *reader) -{ - if (!prop) { - return; - } - - switch (prop->type) { - case IDP_ID: /* PointerProperty */ - { - void *newaddr = BLO_read_get_new_id_address(reader, NULL, IDP_Id(prop)); - if (IDP_Id(prop) && !newaddr && G.debug) { - printf("Error while loading \"%s\". Data not found in file!\n", prop->name); - } - prop->data.pointer = newaddr; - break; - } - case IDP_IDPARRAY: /* CollectionProperty */ - { - IDProperty *idp_array = IDP_IDPArray(prop); - for (int i = 0; i < prop->len; i++) { - IDP_LibLinkProperty(&(idp_array[i]), reader); - } - break; - } - case IDP_GROUP: /* PointerProperty */ - { - LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { - IDP_LibLinkProperty(loop, reader); - } - break; - } - default: - break; /* Nothing to do for other IDProps. */ - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Read Image Preview * \{ */ @@ -2457,8 +2254,7 @@ static PreviewImage *direct_link_preview_image(BlendDataReader *reader, PreviewI PreviewImage *prv = BLO_read_get_new_data_address(reader, old_prv); if (prv) { - int i; - for (i = 0; i < NUM_ICON_SIZES; i++) { + for (int i = 0; i < NUM_ICON_SIZES; i++) { if (prv->rect[i]) { BLO_read_data_address(reader, &prv->rect[i]); } @@ -2504,11 +2300,11 @@ static void lib_link_id(BlendLibReader *reader, ID *id) { /* Note: WM IDProperties are never written to file, hence they should always be NULL here. */ BLI_assert((GS(id->name) != ID_WM) || id->properties == NULL); - IDP_LibLinkProperty(id->properties, reader); + IDP_BlendReadLib(reader, id->properties); AnimData *adt = BKE_animdata_from_id(id); if (adt != NULL) { - lib_link_animdata(reader, id, adt); + BKE_animdata_blend_read_lib(reader, id, adt); } if (id->override_library) { @@ -2635,7 +2431,7 @@ static int direct_link_id_restore_recalc(const FileData *fd, static void direct_link_id_common( BlendDataReader *reader, Library *current_library, ID *id, ID *id_old, const int tag) { - if (reader->fd->memfile == NULL) { + if (!BLO_read_data_is_undo(reader)) { /* When actually reading a file , we do want to reset/re-generate session uuids. * In undo case, we want to re-use existing ones. */ id->session_uuid = MAIN_ID_SESSION_UUID_UNSET; @@ -2663,7 +2459,7 @@ static void direct_link_id_common( if (id->properties) { BLO_read_data_address(reader, &id->properties); /* this case means the data was written incorrectly, it should not happen */ - IDP_DirectLinkGroup_OrFree(&id->properties, reader); + IDP_BlendDataRead(reader, &id->properties); } id->flag &= ~LIB_INDIRECT_WEAK_LINK; @@ -2676,7 +2472,7 @@ static void direct_link_id_common( * * But for regular file load we clear the flag, since the flags might have been changed since * the version the file has been saved with. */ - if (reader->fd->memfile == NULL) { + if (!BLO_read_data_is_undo(reader)) { id->recalc = 0; id->recalc_after_undo_push = 0; } @@ -2836,12 +2632,12 @@ static void direct_link_paint_curve(BlendDataReader *reader, PaintCurve *pc) /** \name Read PackedFile * \{ */ -static PackedFile *direct_link_packedfile(BlendDataReader *reader, PackedFile *oldpf) +static PackedFile *direct_link_packedfile(BlendDataReader *reader, PackedFile *pf) { - PackedFile *pf = newpackedadr(reader->fd, oldpf); + BLO_read_packed_address(reader, &pf); if (pf) { - pf->data = newpackedadr(reader->fd, pf->data); + BLO_read_packed_address(reader, &pf->data); if (pf->data == NULL) { /* We cannot allow a PackedFile with a NULL data field, * the whole code assumes this is not possible. See T70315. */ @@ -2872,11 +2668,9 @@ static void lib_link_ipo(BlendLibReader *reader, Ipo *ipo) // XXX deprecated - old animation system static void direct_link_ipo(BlendDataReader *reader, Ipo *ipo) { - IpoCurve *icu; - BLO_read_list(reader, &(ipo->curve)); - for (icu = ipo->curve.first; icu; icu = icu->next) { + LISTBASE_FOREACH (IpoCurve *, icu, &ipo->curve) { BLO_read_data_address(reader, &icu->bezt); BLO_read_data_address(reader, &icu->bp); BLO_read_data_address(reader, &icu->driver); @@ -2886,14 +2680,11 @@ static void direct_link_ipo(BlendDataReader *reader, Ipo *ipo) // XXX deprecated - old animation system static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *striplist) { - bActionStrip *strip; - bActionModifier *amod; - - for (strip = striplist->first; strip; strip = strip->next) { + LISTBASE_FOREACH (bActionStrip *, strip, striplist) { BLO_read_id_address(reader, id->lib, &strip->object); BLO_read_id_address(reader, id->lib, &strip->act); BLO_read_id_address(reader, id->lib, &strip->ipo); - for (amod = strip->modifiers.first; amod; amod = amod->next) { + LISTBASE_FOREACH (bActionModifier *, amod, &strip->modifiers) { BLO_read_id_address(reader, id->lib, &amod->ob); } } @@ -2902,11 +2693,9 @@ static void lib_link_nlastrips(BlendLibReader *reader, ID *id, ListBase *stripli // XXX deprecated - old animation system static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips) { - bActionStrip *strip; - BLO_read_list(reader, strips); - for (strip = strips->first; strip; strip = strip->next) { + LISTBASE_FOREACH (bActionStrip *, strip, strips) { BLO_read_list(reader, &strip->modifiers); } } @@ -2914,9 +2703,7 @@ static void direct_link_nlastrips(BlendDataReader *reader, ListBase *strips) // XXX deprecated - old animation system static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBase *chanbase) { - bConstraintChannel *chan; - - for (chan = chanbase->first; chan; chan = chan->next) { + LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) { BLO_read_id_address(reader, id->lib, &chan->ipo); } } @@ -2927,153 +2714,6 @@ static void lib_link_constraint_channels(BlendLibReader *reader, ID *id, ListBas /** \name Read ID: Action * \{ */ -static void lib_link_fmodifiers(BlendLibReader *reader, ID *id, ListBase *list) -{ - FModifier *fcm; - - for (fcm = list->first; fcm; fcm = fcm->next) { - /* data for specific modifiers */ - switch (fcm->type) { - case FMODIFIER_TYPE_PYTHON: { - FMod_Python *data = (FMod_Python *)fcm->data; - BLO_read_id_address(reader, id->lib, &data->script); - - break; - } - } - } -} - -static void lib_link_fcurves(BlendLibReader *reader, ID *id, ListBase *list) -{ - FCurve *fcu; - - if (list == NULL) { - return; - } - - /* relink ID-block references... */ - for (fcu = list->first; fcu; fcu = fcu->next) { - /* driver data */ - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* only relink if still used */ - if (tarIndex < dvar->num_targets) { - BLO_read_id_address(reader, id->lib, &dtar->id); - } - else { - dtar->id = NULL; - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - - /* modifiers */ - lib_link_fmodifiers(reader, id, &fcu->modifiers); - } -} - -/* NOTE: this assumes that link_list has already been called on the list */ -static void direct_link_fmodifiers(BlendDataReader *reader, ListBase *list, FCurve *curve) -{ - FModifier *fcm; - - for (fcm = list->first; fcm; fcm = fcm->next) { - /* relink general data */ - BLO_read_data_address(reader, &fcm->data); - fcm->curve = curve; - - /* do relinking of data for specific types */ - switch (fcm->type) { - case FMODIFIER_TYPE_GENERATOR: { - FMod_Generator *data = (FMod_Generator *)fcm->data; - BLO_read_float_array(reader, data->arraysize, &data->coefficients); - break; - } - case FMODIFIER_TYPE_ENVELOPE: { - FMod_Envelope *data = (FMod_Envelope *)fcm->data; - - BLO_read_data_address(reader, &data->data); - - break; - } - case FMODIFIER_TYPE_PYTHON: { - FMod_Python *data = (FMod_Python *)fcm->data; - - BLO_read_data_address(reader, &data->prop); - IDP_DirectLinkGroup_OrFree(&data->prop, reader); - - break; - } - } - } -} - -/* NOTE: this assumes that link_list has already been called on the list */ -static void direct_link_fcurves(BlendDataReader *reader, ListBase *list) -{ - FCurve *fcu; - - /* link F-Curve data to F-Curve again (non ID-libs) */ - for (fcu = list->first; fcu; fcu = fcu->next) { - /* curve data */ - BLO_read_data_address(reader, &fcu->bezt); - BLO_read_data_address(reader, &fcu->fpt); - - /* rna path */ - BLO_read_data_address(reader, &fcu->rna_path); - - /* group */ - BLO_read_data_address(reader, &fcu->grp); - - /* clear disabled flag - allows disabled drivers to be tried again ([#32155]), - * but also means that another method for "reviving disabled F-Curves" exists - */ - fcu->flag &= ~FCURVE_DISABLED; - - /* driver */ - BLO_read_data_address(reader, &fcu->driver); - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - /* Compiled expression data will need to be regenerated - * (old pointer may still be set here). */ - driver->expr_comp = NULL; - driver->expr_simple = NULL; - - /* give the driver a fresh chance - the operating environment may be different now - * (addons, etc. may be different) so the driver namespace may be sane now [#32155] - */ - driver->flag &= ~DRIVER_FLAG_INVALID; - - /* relink variables, targets and their paths */ - BLO_read_list(reader, &driver->variables); - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - /* only relink the targets being used */ - if (tarIndex < dvar->num_targets) { - BLO_read_data_address(reader, &dtar->rna_path); - } - else { - dtar->rna_path = NULL; - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - - /* modifiers */ - BLO_read_list(reader, &fcu->modifiers); - direct_link_fmodifiers(reader, &fcu->modifiers, fcu); - } -} - static void lib_link_action(BlendLibReader *reader, bAction *act) { // XXX deprecated - old animation system <<< @@ -3083,7 +2723,7 @@ static void lib_link_action(BlendLibReader *reader, bAction *act) } // >>> XXX deprecated - old animation system - lib_link_fcurves(reader, &act->id, &act->curves); + BKE_fcurve_blend_read_lib(reader, &act->id, &act->curves); LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { if (marker->camera) { @@ -3094,102 +2734,34 @@ static void lib_link_action(BlendLibReader *reader, bAction *act) static void direct_link_action(BlendDataReader *reader, bAction *act) { - bActionChannel *achan; // XXX deprecated - old animation system - bActionGroup *agrp; - BLO_read_list(reader, &act->curves); BLO_read_list(reader, &act->chanbase); // XXX deprecated - old animation system BLO_read_list(reader, &act->groups); BLO_read_list(reader, &act->markers); // XXX deprecated - old animation system <<< - for (achan = act->chanbase.first; achan; achan = achan->next) { + LISTBASE_FOREACH (bActionChannel *, achan, &act->chanbase) { BLO_read_data_address(reader, &achan->grp); BLO_read_list(reader, &achan->constraintChannels); } // >>> XXX deprecated - old animation system - direct_link_fcurves(reader, &act->curves); + BKE_fcurve_blend_read_data(reader, &act->curves); - for (agrp = act->groups.first; agrp; agrp = agrp->next) { + LISTBASE_FOREACH (bActionGroup *, agrp, &act->groups) { BLO_read_data_address(reader, &agrp->channels.first); BLO_read_data_address(reader, &agrp->channels.last); } } -static void lib_link_nladata_strips(BlendLibReader *reader, ID *id, ListBase *list) -{ - NlaStrip *strip; - - for (strip = list->first; strip; strip = strip->next) { - /* check strip's children */ - lib_link_nladata_strips(reader, id, &strip->strips); - - /* check strip's F-Curves */ - lib_link_fcurves(reader, id, &strip->fcurves); - - /* reassign the counted-reference to action */ - BLO_read_id_address(reader, id->lib, &strip->act); - } -} - -static void lib_link_nladata(BlendLibReader *reader, ID *id, ListBase *list) -{ - NlaTrack *nlt; - - /* we only care about the NLA strips inside the tracks */ - for (nlt = list->first; nlt; nlt = nlt->next) { - lib_link_nladata_strips(reader, id, &nlt->strips); - } -} - -/* This handles Animato NLA-Strips linking - * NOTE: this assumes that link_list has already been called on the list - */ -static void direct_link_nladata_strips(BlendDataReader *reader, ListBase *list) -{ - NlaStrip *strip; - - for (strip = list->first; strip; strip = strip->next) { - /* strip's child strips */ - BLO_read_list(reader, &strip->strips); - direct_link_nladata_strips(reader, &strip->strips); - - /* strip's F-Curves */ - BLO_read_list(reader, &strip->fcurves); - direct_link_fcurves(reader, &strip->fcurves); - - /* strip's F-Modifiers */ - BLO_read_list(reader, &strip->modifiers); - direct_link_fmodifiers(reader, &strip->modifiers, NULL); - } -} - -/* NOTE: this assumes that BLO_read_list has already been called on the list */ -static void direct_link_nladata(BlendDataReader *reader, ListBase *list) -{ - NlaTrack *nlt; - - for (nlt = list->first; nlt; nlt = nlt->next) { - /* relink list of strips */ - BLO_read_list(reader, &nlt->strips); - - /* relink strip data */ - direct_link_nladata_strips(reader, &nlt->strips); - } -} - /* ------- */ static void lib_link_keyingsets(BlendLibReader *reader, ID *id, ListBase *list) { - KeyingSet *ks; - KS_Path *ksp; - /* here, we're only interested in the ID pointer stored in some of the paths */ - for (ks = list->first; ks; ks = ks->next) { - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + LISTBASE_FOREACH (KeyingSet *, ks, list) { + LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { BLO_read_id_address(reader, id->lib, &ksp->id); } } @@ -3198,71 +2770,18 @@ static void lib_link_keyingsets(BlendLibReader *reader, ID *id, ListBase *list) /* NOTE: this assumes that BLO_read_list has already been called on the list */ static void direct_link_keyingsets(BlendDataReader *reader, ListBase *list) { - KeyingSet *ks; - KS_Path *ksp; - /* link KeyingSet data to KeyingSet again (non ID-libs) */ - for (ks = list->first; ks; ks = ks->next) { + LISTBASE_FOREACH (KeyingSet *, ks, list) { /* paths */ BLO_read_list(reader, &ks->paths); - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { /* rna path */ BLO_read_data_address(reader, &ksp->rna_path); } } } -/* ------- */ - -static void lib_link_animdata(BlendLibReader *reader, ID *id, AnimData *adt) -{ - if (adt == NULL) { - return; - } - - /* link action data */ - BLO_read_id_address(reader, id->lib, &adt->action); - BLO_read_id_address(reader, id->lib, &adt->tmpact); - - /* link drivers */ - lib_link_fcurves(reader, id, &adt->drivers); - - /* overrides don't have lib-link for now, so no need to do anything */ - - /* link NLA-data */ - lib_link_nladata(reader, id, &adt->nla_tracks); -} - -static void direct_link_animdata(BlendDataReader *reader, AnimData *adt) -{ - /* NOTE: must have called newdataadr already before doing this... */ - if (adt == NULL) { - return; - } - - /* link drivers */ - BLO_read_list(reader, &adt->drivers); - direct_link_fcurves(reader, &adt->drivers); - adt->driver_array = NULL; - - /* link overrides */ - // TODO... - - /* link NLA-data */ - BLO_read_list(reader, &adt->nla_tracks); - direct_link_nladata(reader, &adt->nla_tracks); - - /* relink active track/strip - even though strictly speaking this should only be used - * if we're in 'tweaking mode', we need to be able to have this loaded back for - * undo, but also since users may not exit tweakmode before saving (#24535) - */ - // TODO: it's not really nice that anyone should be able to save the file in this - // state, but it's going to be too hard to enforce this single case... - BLO_read_data_address(reader, &adt->act_track); - BLO_read_data_address(reader, &adt->actstrip); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -3282,7 +2801,7 @@ static void direct_link_cachefile(BlendDataReader *reader, CacheFile *cache_file /* relink animdata */ BLO_read_data_address(reader, &cache_file->adt); - direct_link_animdata(reader, cache_file->adt); + BKE_animdata_blend_read_data(reader, cache_file->adt); } /** \} */ @@ -3330,7 +2849,7 @@ static void direct_link_workspace(BlendDataReader *reader, WorkSpace *workspace, /* Same issue/fix as in direct_link_workspace_link_scene_data: Can't read workspace data * when reading windows, so have to update windows after/when reading workspaces. */ - for (wmWindowManager *wm = main->wm.first; wm; wm = wm->id.next) { + LISTBASE_FOREACH (wmWindowManager *, wm, &main->wm) { LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { BLO_read_data_address(reader, &win->workspace_hook->act_layout); } @@ -3339,7 +2858,7 @@ static void direct_link_workspace(BlendDataReader *reader, WorkSpace *workspace, LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { tref->runtime = NULL; BLO_read_data_address(reader, &tref->properties); - IDP_DirectLinkGroup_OrFree(&tref->properties, reader); + IDP_BlendDataRead(reader, &tref->properties); } workspace->status_text = NULL; @@ -3364,7 +2883,7 @@ static void lib_link_workspace_instance_hook(BlendLibReader *reader, static void lib_link_node_socket(BlendLibReader *reader, Library *lib, bNodeSocket *sock) { - IDP_LibLinkProperty(sock->prop, reader); + IDP_BlendReadLib(reader, sock->prop); switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { @@ -3411,7 +2930,7 @@ static void lib_link_ntree(BlendLibReader *reader, Library *lib, bNodeTree *ntre LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { /* Link ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_LibLinkProperty(node->prop, reader); + IDP_BlendReadLib(reader, node->prop); BLO_read_id_address(reader, lib, &node->id); @@ -3430,7 +2949,7 @@ static void lib_link_ntree(BlendLibReader *reader, Library *lib, bNodeTree *ntre /* For nodes with static socket layout, add/remove sockets as needed * to match the static layout. */ - if (reader->fd->memfile == NULL) { + if (!BLO_read_lib_is_undo(reader)) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node_verify_socket_templates(ntree, node); } @@ -3446,7 +2965,7 @@ static void lib_link_nodetree(BlendLibReader *reader, bNodeTree *ntree) static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) { BLO_read_data_address(reader, &sock->prop); - IDP_DirectLinkGroup_OrFree(&sock->prop, reader); + IDP_BlendDataRead(reader, &sock->prop); BLO_read_data_address(reader, &sock->link); sock->typeinfo = NULL; @@ -3459,9 +2978,6 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock) static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) { /* note: writing and reading goes in sync, for speed */ - bNode *node; - bNodeSocket *sock; - bNodeLink *link; ntree->init = 0; /* to set callbacks and force setting types */ ntree->is_updating = false; @@ -3472,20 +2988,20 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) ntree->execdata = NULL; BLO_read_data_address(reader, &ntree->adt); - direct_link_animdata(reader, ntree->adt); + BKE_animdata_blend_read_data(reader, ntree->adt); BLO_read_list(reader, &ntree->nodes); - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->typeinfo = NULL; BLO_read_list(reader, &node->inputs); BLO_read_list(reader, &node->outputs); BLO_read_data_address(reader, &node->prop); - IDP_DirectLinkGroup_OrFree(&node->prop, reader); + IDP_BlendDataRead(reader, &node->prop); BLO_read_list(reader, &node->internal_links); - for (link = node->internal_links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) { BLO_read_data_address(reader, &link->fromnode); BLO_read_data_address(reader, &link->fromsock); BLO_read_data_address(reader, &link->tonode); @@ -3564,14 +3080,14 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) BLO_read_list(reader, &ntree->links); /* and we connect the rest */ - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BLO_read_data_address(reader, &node->parent); node->lasty = 0; - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { direct_link_node_socket(reader, sock); } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { direct_link_node_socket(reader, sock); } } @@ -3579,14 +3095,14 @@ static void direct_link_nodetree(BlendDataReader *reader, bNodeTree *ntree) /* interface socket lists */ BLO_read_list(reader, &ntree->inputs); BLO_read_list(reader, &ntree->outputs); - for (sock = ntree->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { direct_link_node_socket(reader, sock); } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { direct_link_node_socket(reader, sock); } - for (link = ntree->links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { BLO_read_data_address(reader, &link->fromnode); BLO_read_data_address(reader, &link->tonode); BLO_read_data_address(reader, &link->fromsock); @@ -3623,10 +3139,9 @@ static void lib_link_constraint_cb(bConstraint *UNUSED(con), static void lib_link_constraints(BlendLibReader *reader, ID *id, ListBase *conlist) { tConstraintLinkData cld; - bConstraint *con; /* legacy fixes */ - for (con = conlist->first; con; con = con->next) { + LISTBASE_FOREACH (bConstraint *, con, conlist) { /* patch for error introduced by changing constraints (dunno how) */ /* if con->data type changes, dna cannot resolve the pointer! (ton) */ if (con->data == NULL) { @@ -3650,10 +3165,8 @@ static void lib_link_constraints(BlendLibReader *reader, ID *id, ListBase *conli static void direct_link_constraints(BlendDataReader *reader, ListBase *lb) { - bConstraint *con; - BLO_read_list(reader, lb); - for (con = lb->first; con; con = con->next) { + LISTBASE_FOREACH (bConstraint *, con, lb) { BLO_read_data_address(reader, &con->data); switch (con->type) { @@ -3663,7 +3176,7 @@ static void direct_link_constraints(BlendDataReader *reader, ListBase *lb) BLO_read_list(reader, &data->targets); BLO_read_data_address(reader, &data->prop); - IDP_DirectLinkGroup_OrFree(&data->prop, reader); + IDP_BlendDataRead(reader, &data->prop); break; } case CONSTRAINT_TYPE_ARMATURE: { @@ -3716,7 +3229,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose) /* always rebuild to match proxy or lib changes, but on Undo */ bool rebuild = false; - if (reader->fd->memfile == NULL) { + if (!BLO_read_lib_is_undo(reader)) { if (ob->proxy || ob->id.lib != arm->id.lib) { rebuild = true; } @@ -3742,7 +3255,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose) pchan->bone = BKE_armature_find_bone_name(arm, pchan->name); - IDP_LibLinkProperty(pchan->prop, reader); + IDP_BlendReadLib(reader, pchan->prop); BLO_read_id_address(reader, arm->id.lib, &pchan->custom); if (UNLIKELY(pchan->bone == NULL)) { @@ -3764,7 +3277,7 @@ static void lib_link_pose(BlendLibReader *reader, Object *ob, bPose *pose) static void lib_link_bones(BlendLibReader *reader, Bone *bone) { - IDP_LibLinkProperty(bone->prop, reader); + IDP_BlendReadLib(reader, bone->prop); LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) { lib_link_bones(reader, curbone); @@ -3780,11 +3293,9 @@ static void lib_link_armature(BlendLibReader *reader, bArmature *arm) static void direct_link_bones(BlendDataReader *reader, Bone *bone) { - Bone *child; - BLO_read_data_address(reader, &bone->parent); BLO_read_data_address(reader, &bone->prop); - IDP_DirectLinkGroup_OrFree(&bone->prop, reader); + IDP_BlendDataRead(reader, &bone->prop); BLO_read_data_address(reader, &bone->bbone_next); BLO_read_data_address(reader, &bone->bbone_prev); @@ -3793,15 +3304,13 @@ static void direct_link_bones(BlendDataReader *reader, Bone *bone) BLO_read_list(reader, &bone->childbase); - for (child = bone->childbase.first; child; child = child->next) { + LISTBASE_FOREACH (Bone *, child, &bone->childbase) { direct_link_bones(reader, child); } } static void direct_link_armature(BlendDataReader *reader, bArmature *arm) { - Bone *bone; - BLO_read_list(reader, &arm->bonebase); arm->bonehash = NULL; arm->edbo = NULL; @@ -3809,9 +3318,9 @@ static void direct_link_armature(BlendDataReader *reader, bArmature *arm) arm->needs_flush_to_id = 0; BLO_read_data_address(reader, &arm->adt); - direct_link_animdata(reader, arm->adt); + BKE_animdata_blend_read_data(reader, arm->adt); - for (bone = arm->bonebase.first; bone; bone = bone->next) { + LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { direct_link_bones(reader, bone); } @@ -3843,7 +3352,7 @@ static void lib_link_camera(BlendLibReader *reader, Camera *ca) static void direct_link_camera(BlendDataReader *reader, Camera *ca) { BLO_read_data_address(reader, &ca->adt); - direct_link_animdata(reader, ca->adt); + BKE_animdata_blend_read_data(reader, ca->adt); BLO_read_list(reader, &ca->bg_images); @@ -3867,7 +3376,7 @@ static void lib_link_light(BlendLibReader *reader, Light *la) static void direct_link_light(BlendDataReader *reader, Light *la) { BLO_read_data_address(reader, &la->adt); - direct_link_animdata(reader, la->adt); + BKE_animdata_blend_read_data(reader, la->adt); BLO_read_data_address(reader, &la->curfalloff); if (la->curfalloff) { @@ -3885,10 +3394,8 @@ static void direct_link_light(BlendDataReader *reader, Light *la) void blo_do_versions_key_uidgen(Key *key) { - KeyBlock *block; - key->uidgen = 1; - for (block = key->block.first; block; block = block->next) { + LISTBASE_FOREACH (KeyBlock *, block, &key->block) { block->uid = key->uidgen++; } } @@ -3903,13 +3410,10 @@ static void lib_link_key(BlendLibReader *reader, Key *key) static void switch_endian_keyblock(Key *key, KeyBlock *kb) { - int elemsize, a, b; - char *data; + int elemsize = key->elemsize; + char *data = kb->data; - elemsize = key->elemsize; - data = kb->data; - - for (a = 0; a < kb->totelem; a++) { + for (int a = 0; a < kb->totelem; a++) { const char *cp = key->elemstr; char *poin = data; @@ -3917,11 +3421,12 @@ static void switch_endian_keyblock(Key *key, KeyBlock *kb) switch (cp[1]) { /* cp[1] = type */ case IPO_FLOAT: case IPO_BPOINT: - case IPO_BEZTRIPLE: - b = cp[0]; + case IPO_BEZTRIPLE: { + int b = cp[0]; BLI_endian_switch_float_array((float *)poin, b); poin += sizeof(float) * b; break; + } } cp += 2; @@ -3932,16 +3437,14 @@ static void switch_endian_keyblock(Key *key, KeyBlock *kb) static void direct_link_key(BlendDataReader *reader, Key *key) { - KeyBlock *kb; - BLO_read_list(reader, &(key->block)); BLO_read_data_address(reader, &key->adt); - direct_link_animdata(reader, key->adt); + BKE_animdata_blend_read_data(reader, key->adt); BLO_read_data_address(reader, &key->refkey); - for (kb = key->block.first; kb; kb = kb->next) { + LISTBASE_FOREACH (KeyBlock *, kb, &key->block) { BLO_read_data_address(reader, &kb->data); if (BLO_read_requires_endian_switch(reader)) { @@ -3968,7 +3471,7 @@ static void lib_link_mball(BlendLibReader *reader, MetaBall *mb) static void direct_link_mball(BlendDataReader *reader, MetaBall *mb) { BLO_read_data_address(reader, &mb->adt); - direct_link_animdata(reader, mb->adt); + BKE_animdata_blend_read_data(reader, mb->adt); BLO_read_pointer_array(reader, (void **)&mb->mat); @@ -3997,7 +3500,7 @@ static void lib_link_world(BlendLibReader *reader, World *wrld) static void direct_link_world(BlendDataReader *reader, World *wrld) { BLO_read_data_address(reader, &wrld->adt); - direct_link_animdata(reader, wrld->adt); + BKE_animdata_blend_read_data(reader, wrld->adt); wrld->preview = direct_link_preview_image(reader, wrld->preview); BLI_listbase_clear(&wrld->gpumaterial); @@ -4032,8 +3535,6 @@ static void lib_link_text(BlendLibReader *UNUSED(reader), Text *UNUSED(text)) static void direct_link_text(BlendDataReader *reader, Text *text) { - TextLine *ln; - BLO_read_data_address(reader, &text->filepath); text->compiled = NULL; @@ -4050,7 +3551,7 @@ static void direct_link_text(BlendDataReader *reader, Text *text) BLO_read_data_address(reader, &text->curl); BLO_read_data_address(reader, &text->sell); - for (ln = text->lines.first; ln; ln = ln->next) { + LISTBASE_FOREACH (TextLine *, ln, &text->lines) { BLO_read_data_address(reader, &ln->line); ln->format = NULL; @@ -4084,12 +3585,10 @@ static void lib_link_image(BlendLibReader *UNUSED(reader), Image *ima) static void direct_link_image(BlendDataReader *reader, Image *ima) { - ImagePackedFile *imapf; - BLO_read_list(reader, &ima->tiles); BLO_read_list(reader, &(ima->renderslots)); - if (reader->fd->memfile == NULL) { + if (!BLO_read_data_is_undo(reader)) { /* We reset this last render slot index only when actually reading a file, not for undo. */ ima->last_render_slot = ima->render_slot; } @@ -4098,7 +3597,7 @@ static void direct_link_image(BlendDataReader *reader, Image *ima) BLO_read_list(reader, &(ima->packedfiles)); if (ima->packedfiles.first) { - for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + LISTBASE_FOREACH (ImagePackedFile *, imapf, &ima->packedfiles) { imapf->packedfile = direct_link_packedfile(reader, imapf->packedfile); } ima->packedfile = NULL; @@ -4151,11 +3650,8 @@ static void switch_endian_knots(Nurb *nu) static void direct_link_curve(BlendDataReader *reader, Curve *cu) { - Nurb *nu; - TextBox *tb; - BLO_read_data_address(reader, &cu->adt); - direct_link_animdata(reader, cu->adt); + BKE_animdata_blend_read_data(reader, cu->adt); /* Protect against integer overflow vulnerability. */ CLAMP(cu->len_char32, 0, INT_MAX - 4); @@ -4172,7 +3668,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu) else { cu->nurb.first = cu->nurb.last = NULL; - tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "TextBoxread"); + TextBox *tb = MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), "TextBoxread"); if (cu->tb) { memcpy(tb, cu->tb, cu->totbox * sizeof(TextBox)); MEM_freeN(cu->tb); @@ -4193,7 +3689,7 @@ static void direct_link_curve(BlendDataReader *reader, Curve *cu) cu->editfont = NULL; cu->batch_cache = NULL; - for (nu = cu->nurb.first; nu; nu = nu->next) { + LISTBASE_FOREACH (Nurb *, nu, &cu->nurb) { BLO_read_data_address(reader, &nu->bezt); BLO_read_data_address(reader, &nu->bp); BLO_read_data_address(reader, &nu->knotsu); @@ -4224,7 +3720,7 @@ static void lib_link_texture(BlendLibReader *reader, Tex *tex) static void direct_link_texture(BlendDataReader *reader, Tex *tex) { BLO_read_data_address(reader, &tex->adt); - direct_link_animdata(reader, tex->adt); + BKE_animdata_blend_read_data(reader, tex->adt); BLO_read_data_address(reader, &tex->coba); @@ -4259,7 +3755,7 @@ static void lib_link_material(BlendLibReader *reader, Material *ma) static void direct_link_material(BlendDataReader *reader, Material *ma) { BLO_read_data_address(reader, &ma->adt); - direct_link_animdata(reader, ma->adt); + BKE_animdata_blend_read_data(reader, ma->adt); ma->texpaintslot = NULL; @@ -4290,9 +3786,7 @@ static const char *ptcache_data_struct[] = { static void direct_link_pointcache_cb(BlendDataReader *reader, void *data) { PTCacheMem *pm = data; - PTCacheExtra *extra; - int i; - for (i = 0; i < BPHYS_TOT_DATA; i++) { + for (int i = 0; i < BPHYS_TOT_DATA; i++) { BLO_read_data_address(reader, &pm->data[i]); /* the cache saves non-struct data without DNA */ @@ -4309,7 +3803,7 @@ static void direct_link_pointcache_cb(BlendDataReader *reader, void *data) BLO_read_list(reader, &pm->extradata); - for (extra = pm->extradata.first; extra; extra = extra->next) { + LISTBASE_FOREACH (PTCacheExtra *, extra, &pm->extradata) { BLO_read_data_address(reader, &extra->data); } } @@ -4337,9 +3831,8 @@ static void direct_link_pointcache_list(BlendDataReader *reader, int force_disk) { if (ptcaches->first) { - PointCache *cache = NULL; BLO_read_list(reader, ptcaches); - for (cache = ptcaches->first; cache; cache = cache->next) { + LISTBASE_FOREACH (PointCache *, cache, ptcaches) { direct_link_pointcache(reader, cache); if (force_disk) { cache->flag |= PTCACHE_DISK_CACHE; @@ -4402,11 +3895,8 @@ static void lib_link_particlesettings(BlendLibReader *reader, ParticleSettings * } if (part->boids) { - BoidState *state = part->boids->states.first; - BoidRule *rule; - for (; state; state = state->next) { - rule = state->rules.first; - for (; rule; rule = rule->next) { + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { + LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { switch (rule->type) { case eBoidRuleType_Goal: case eBoidRuleType_Avoid: { @@ -4442,13 +3932,11 @@ static void direct_link_partdeflect(PartDeflect *pd) static void direct_link_particlesettings(BlendDataReader *reader, ParticleSettings *part) { - int a; - BLO_read_data_address(reader, &part->adt); BLO_read_data_address(reader, &part->pd); BLO_read_data_address(reader, &part->pd2); - direct_link_animdata(reader, part->adt); + BKE_animdata_blend_read_data(reader, part->adt); direct_link_partdeflect(part->pd); direct_link_partdeflect(part->pd2); @@ -4476,16 +3964,15 @@ static void direct_link_particlesettings(BlendDataReader *reader, ParticleSettin BLO_read_data_address(reader, &part->fluid); if (part->boids) { - BoidState *state; BLO_read_list(reader, &part->boids->states); - for (state = part->boids->states.first; state; state = state->next) { + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { BLO_read_list(reader, &state->rules); BLO_read_list(reader, &state->conditions); BLO_read_list(reader, &state->actions); } } - for (a = 0; a < MAX_MTEX; a++) { + for (int a = 0; a < MAX_MTEX; a++) { BLO_read_data_address(reader, &part->mtex[a]); } @@ -4498,16 +3985,11 @@ static void lib_link_particlesystems(BlendLibReader *reader, ID *id, ListBase *particles) { - ParticleSystem *psys, *psysnext; - - for (psys = particles->first; psys; psys = psysnext) { - psysnext = psys->next; + LISTBASE_FOREACH_MUTABLE (ParticleSystem *, psys, particles) { BLO_read_id_address(reader, id->lib, &psys->part); if (psys->part) { - ParticleTarget *pt = psys->targets.first; - - for (; pt; pt = pt->next) { + LISTBASE_FOREACH (ParticleTarget *, pt, &psys->targets) { BLO_read_id_address(reader, id->lib, &pt->ob); } @@ -4536,11 +4018,10 @@ static void lib_link_particlesystems(BlendLibReader *reader, } static void direct_link_particlesystems(BlendDataReader *reader, ListBase *particles) { - ParticleSystem *psys; ParticleData *pa; int a; - for (psys = particles->first; psys; psys = psys->next) { + LISTBASE_FOREACH (ParticleSystem *, psys, particles) { BLO_read_data_address(reader, &psys->particles); if (psys->particles && psys->particles->hair) { @@ -4629,255 +4110,6 @@ static void direct_link_particlesystems(BlendDataReader *reader, ListBase *parti /** \name Read ID: Mesh * \{ */ -static void lib_link_mesh(BlendLibReader *reader, Mesh *me) -{ - /* this check added for python created meshes */ - if (me->mat) { - for (int i = 0; i < me->totcol; i++) { - BLO_read_id_address(reader, me->id.lib, &me->mat[i]); - } - } - else { - me->totcol = 0; - } - - BLO_read_id_address(reader, me->id.lib, &me->ipo); // XXX: deprecated: old anim sys - BLO_read_id_address(reader, me->id.lib, &me->key); - BLO_read_id_address(reader, me->id.lib, &me->texcomesh); -} - -static void direct_link_dverts(BlendDataReader *reader, int count, MDeformVert *mdverts) -{ - int i; - - if (mdverts == NULL) { - return; - } - - for (i = count; i > 0; i--, mdverts++) { - /*convert to vgroup allocation system*/ - MDeformWeight *dw; - if (mdverts->dw && (dw = BLO_read_get_new_data_address(reader, mdverts->dw))) { - const ssize_t dw_len = mdverts->totweight * sizeof(MDeformWeight); - void *dw_tmp = MEM_mallocN(dw_len, "direct_link_dverts"); - memcpy(dw_tmp, dw, dw_len); - mdverts->dw = dw_tmp; - MEM_freeN(dw); - } - else { - mdverts->dw = NULL; - mdverts->totweight = 0; - } - } -} - -static void direct_link_mdisps(BlendDataReader *reader, int count, MDisps *mdisps, int external) -{ - if (mdisps) { - int i; - - for (i = 0; i < count; i++) { - BLO_read_data_address(reader, &mdisps[i].disps); - BLO_read_data_address(reader, &mdisps[i].hidden); - - if (mdisps[i].totdisp && !mdisps[i].level) { - /* this calculation is only correct for loop mdisps; - * if loading pre-BMesh face mdisps this will be - * overwritten with the correct value in - * bm_corners_to_loops() */ - float gridsize = sqrtf(mdisps[i].totdisp); - mdisps[i].level = (int)(logf(gridsize - 1.0f) / (float)M_LN2) + 1; - } - - if (BLO_read_requires_endian_switch(reader) && (mdisps[i].disps)) { - /* DNA_struct_switch_endian doesn't do endian swap for (*disps)[] */ - /* this does swap for data written at write_mdisps() - readfile.c */ - BLI_endian_switch_float_array(*mdisps[i].disps, mdisps[i].totdisp * 3); - } - if (!external && !mdisps[i].disps) { - mdisps[i].totdisp = 0; - } - } - } -} - -static void direct_link_grid_paint_mask(BlendDataReader *reader, - int count, - GridPaintMask *grid_paint_mask) -{ - if (grid_paint_mask) { - int i; - - for (i = 0; i < count; i++) { - GridPaintMask *gpm = &grid_paint_mask[i]; - if (gpm->data) { - BLO_read_data_address(reader, &gpm->data); - } - } - } -} - -/*this isn't really a public api function, so prototyped here*/ -static void direct_link_customdata(BlendDataReader *reader, CustomData *data, int count) -{ - int i = 0; - - BLO_read_data_address(reader, &data->layers); - - /* annoying workaround for bug [#31079] loading legacy files with - * no polygons _but_ have stale customdata */ - if (UNLIKELY(count == 0 && data->layers == NULL && data->totlayer != 0)) { - CustomData_reset(data); - return; - } - - BLO_read_data_address(reader, &data->external); - - while (i < data->totlayer) { - CustomDataLayer *layer = &data->layers[i]; - - if (layer->flag & CD_FLAG_EXTERNAL) { - layer->flag &= ~CD_FLAG_IN_MEMORY; - } - - layer->flag &= ~CD_FLAG_NOFREE; - - if (CustomData_verify_versions(data, i)) { - BLO_read_data_address(reader, &layer->data); - if (layer->type == CD_MDISPS) { - direct_link_mdisps(reader, count, layer->data, layer->flag & CD_FLAG_EXTERNAL); - } - else if (layer->type == CD_GRID_PAINT_MASK) { - direct_link_grid_paint_mask(reader, count, layer->data); - } - i++; - } - } - - CustomData_update_typemap(data); -} - -static void direct_link_mesh(BlendDataReader *reader, Mesh *mesh) -{ - BLO_read_pointer_array(reader, (void **)&mesh->mat); - - BLO_read_data_address(reader, &mesh->mvert); - BLO_read_data_address(reader, &mesh->medge); - BLO_read_data_address(reader, &mesh->mface); - BLO_read_data_address(reader, &mesh->mloop); - BLO_read_data_address(reader, &mesh->mpoly); - BLO_read_data_address(reader, &mesh->tface); - BLO_read_data_address(reader, &mesh->mtface); - BLO_read_data_address(reader, &mesh->mcol); - BLO_read_data_address(reader, &mesh->dvert); - BLO_read_data_address(reader, &mesh->mloopcol); - BLO_read_data_address(reader, &mesh->mloopuv); - BLO_read_data_address(reader, &mesh->mselect); - - /* animdata */ - BLO_read_data_address(reader, &mesh->adt); - direct_link_animdata(reader, mesh->adt); - - /* Normally direct_link_dverts should be called in direct_link_customdata, - * but for backwards compatibility in do_versions to work we do it here. */ - direct_link_dverts(reader, mesh->totvert, mesh->dvert); - - direct_link_customdata(reader, &mesh->vdata, mesh->totvert); - direct_link_customdata(reader, &mesh->edata, mesh->totedge); - direct_link_customdata(reader, &mesh->fdata, mesh->totface); - direct_link_customdata(reader, &mesh->ldata, mesh->totloop); - direct_link_customdata(reader, &mesh->pdata, mesh->totpoly); - - mesh->texflag &= ~ME_AUTOSPACE_EVALUATED; - mesh->edit_mesh = NULL; - BKE_mesh_runtime_reset(mesh); - - /* happens with old files */ - if (mesh->mselect == NULL) { - mesh->totselect = 0; - } - - /* Multires data */ - BLO_read_data_address(reader, &mesh->mr); - if (mesh->mr) { - MultiresLevel *lvl; - - BLO_read_list(reader, &mesh->mr->levels); - lvl = mesh->mr->levels.first; - - direct_link_customdata(reader, &mesh->mr->vdata, lvl->totvert); - direct_link_dverts(reader, lvl->totvert, CustomData_get(&mesh->mr->vdata, 0, CD_MDEFORMVERT)); - direct_link_customdata(reader, &mesh->mr->fdata, lvl->totface); - - BLO_read_data_address(reader, &mesh->mr->edge_flags); - BLO_read_data_address(reader, &mesh->mr->edge_creases); - - BLO_read_data_address(reader, &mesh->mr->verts); - - /* If mesh has the same number of vertices as the - * highest multires level, load the current mesh verts - * into multires and discard the old data. Needed - * because some saved files either do not have a verts - * array, or the verts array contains out-of-date - * data. */ - if (mesh->totvert == ((MultiresLevel *)mesh->mr->levels.last)->totvert) { - if (mesh->mr->verts) { - MEM_freeN(mesh->mr->verts); - } - mesh->mr->verts = MEM_dupallocN(mesh->mvert); - } - - for (; lvl; lvl = lvl->next) { - BLO_read_data_address(reader, &lvl->verts); - BLO_read_data_address(reader, &lvl->faces); - BLO_read_data_address(reader, &lvl->edges); - BLO_read_data_address(reader, &lvl->colfaces); - } - } - - /* if multires is present but has no valid vertex data, - * there's no way to recover it; silently remove multires */ - if (mesh->mr && !mesh->mr->verts) { - multires_free(mesh->mr); - mesh->mr = NULL; - } - - if ((BLO_read_requires_endian_switch(reader)) && mesh->tface) { - TFace *tf = mesh->tface; - int i; - - for (i = 0; i < mesh->totface; i++, tf++) { - BLI_endian_switch_uint32_array(tf->col, 4); - } - } -} - -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Read ID: Lattice - * \{ */ - -static void lib_link_latt(BlendLibReader *reader, Lattice *lt) -{ - BLO_read_id_address(reader, lt->id.lib, <->ipo); // XXX deprecated - old animation system - BLO_read_id_address(reader, lt->id.lib, <->key); -} - -static void direct_link_latt(BlendDataReader *reader, Lattice *lt) -{ - BLO_read_data_address(reader, <->def); - - BLO_read_data_address(reader, <->dvert); - direct_link_dverts(reader, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); - - lt->editlatt = NULL; - lt->batch_cache = NULL; - - BLO_read_data_address(reader, <->adt); - direct_link_animdata(reader, lt->adt); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -4900,7 +4132,7 @@ static void lib_link_modifiers(BlendLibReader *reader, Object *ob) /* If linking from a library, clear 'local' library override flag. */ if (ob->id.lib != NULL) { - for (ModifierData *mod = ob->modifiers.first; mod != NULL; mod = mod->next) { + LISTBASE_FOREACH (ModifierData *, mod, &ob->modifiers) { mod->flag &= ~eModifierFlag_OverrideLibrary_Local; } } @@ -4912,8 +4144,7 @@ static void lib_link_gpencil_modifiers(BlendLibReader *reader, Object *ob) /* If linking from a library, clear 'local' library override flag. */ if (ob->id.lib != NULL) { - for (GpencilModifierData *mod = ob->greasepencil_modifiers.first; mod != NULL; - mod = mod->next) { + LISTBASE_FOREACH (GpencilModifierData *, mod, &ob->greasepencil_modifiers) { mod->flag &= ~eGpencilModifierFlag_OverrideLibrary_Local; } } @@ -4925,7 +4156,7 @@ static void lib_link_shaderfxs(BlendLibReader *reader, Object *ob) /* If linking from a library, clear 'local' library override flag. */ if (ob->id.lib != NULL) { - for (ShaderFxData *fx = ob->shader_fx.first; fx != NULL; fx = fx->next) { + LISTBASE_FOREACH (ShaderFxData *, fx, &ob->shader_fx) { fx->flag &= ~eShaderFxFlag_OverrideLibrary_Local; } } @@ -4934,7 +4165,6 @@ static void lib_link_shaderfxs(BlendLibReader *reader, Object *ob) static void lib_link_object(BlendLibReader *reader, Object *ob) { bool warn = false; - int a; // XXX deprecated - old animation system <<< BLO_read_id_address(reader, ob->id.lib, &ob->ipo); @@ -5013,7 +4243,7 @@ static void lib_link_object(BlendLibReader *reader, Object *ob) ob->mode &= ~OB_MODE_POSE; } } - for (a = 0; a < ob->totcol; a++) { + for (int a = 0; a < ob->totcol; a++) { BLO_read_id_address(reader, ob->id.lib, &ob->mat[a]); } @@ -5094,17 +4324,6 @@ static void lib_link_object(BlendLibReader *reader, Object *ob) BLO_read_id_address(reader, ob->id.lib, &ob->rigidbody_constraint->ob2); } - { - LodLevel *level; - for (level = ob->lodlevels.first; level; level = level->next) { - BLO_read_id_address(reader, ob->id.lib, &level->source); - - if (!level->source && level == ob->lodlevels.first) { - level->source = ob; - } - } - } - if (warn) { BKE_report(reader->fd->reports, RPT_WARNING, "Warning in console"); } @@ -5128,8 +4347,6 @@ static void direct_link_motionpath(BlendDataReader *reader, bMotionPath *mpath) static void direct_link_pose(BlendDataReader *reader, bPose *pose) { - bPoseChannel *pchan; - if (!pose) { return; } @@ -5140,7 +4357,7 @@ static void direct_link_pose(BlendDataReader *reader, bPose *pose) pose->chanhash = NULL; pose->chan_array = NULL; - for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) { + LISTBASE_FOREACH (bPoseChannel *, pchan, &pose->chanbase) { BKE_pose_channel_runtime_reset(&pchan->runtime); BKE_pose_channel_session_uuid_generate(pchan); @@ -5155,7 +4372,7 @@ static void direct_link_pose(BlendDataReader *reader, bPose *pose) direct_link_constraints(reader, &pchan->constraints); BLO_read_data_address(reader, &pchan->prop); - IDP_DirectLinkGroup_OrFree(&pchan->prop, reader); + IDP_BlendDataRead(reader, &pchan->prop); BLO_read_data_address(reader, &pchan->mpath); if (pchan->mpath) { @@ -5292,11 +4509,9 @@ static ModifierData *modifier_replace_with_fluid(FileData *fd, static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object *ob) { - ModifierData *md; - BLO_read_list(reader, lb); - for (md = lb->first; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, lb) { BKE_modifier_session_uuid_generate(md); md->error = NULL; @@ -5453,10 +4668,9 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object pmd->canvas->flags &= ~MOD_DPAINT_BAKING; /* just in case */ if (pmd->canvas->surfaces.first) { - DynamicPaintSurface *surface; BLO_read_list(reader, &pmd->canvas->surfaces); - for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { surface->canvas = pmd->canvas; surface->data = NULL; direct_link_pointcache_list(reader, &(surface->ptcaches), &(surface->pointcache), 1); @@ -5485,11 +4699,9 @@ static void direct_link_modifiers(BlendDataReader *reader, ListBase *lb, Object static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) { - GpencilModifierData *md; - BLO_read_list(reader, lb); - for (md = lb->first; md; md = md->next) { + LISTBASE_FOREACH (GpencilModifierData *, md, lb) { md->error = NULL; /* if modifiers disappear, or for upward compatibility */ @@ -5566,11 +4778,9 @@ static void direct_link_gpencil_modifiers(BlendDataReader *reader, ListBase *lb) static void direct_link_shaderfxs(BlendDataReader *reader, ListBase *lb) { - ShaderFxData *fx; - BLO_read_list(reader, lb); - for (fx = lb->first; fx; fx = fx->next) { + LISTBASE_FOREACH (ShaderFxData *, fx, lb) { fx->error = NULL; /* if shader disappear, or for upward compatibility */ @@ -5594,15 +4804,16 @@ static void direct_link_object(BlendDataReader *reader, Object *ob) * Also when linking in a file don't allow edit and pose modes. * See [#34776, #42780] for more information. */ - if (reader->fd->memfile || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) { + const bool is_undo = BLO_read_data_is_undo(reader); + if (is_undo || (ob->id.tag & (LIB_TAG_EXTERN | LIB_TAG_INDIRECT))) { ob->mode &= ~(OB_MODE_EDIT | OB_MODE_PARTICLE_EDIT); - if (!reader->fd->memfile) { + if (!is_undo) { ob->mode &= ~OB_MODE_POSE; } } BLO_read_data_address(reader, &ob->adt); - direct_link_animdata(reader, ob->adt); + BKE_animdata_blend_read_data(reader, ob->adt); BLO_read_data_address(reader, &ob->pose); direct_link_pose(reader, ob->pose); @@ -5684,20 +4895,16 @@ static void direct_link_object(BlendDataReader *reader, Object *ob) if (ob->soft) { SoftBody *sb = ob->soft; - sb->bpoint = NULL; // init pointers so it gets rebuilt nicely + /* init pointers so it gets rebuilt nicely */ + sb->bpoint = NULL; sb->bspring = NULL; sb->scratch = NULL; - sb->admmpd = NULL; - - /* Re-alloc or read custom data structs */ - sbCustomRead(ob); /* although not used anymore */ /* still have to be loaded to be compatible with old files */ BLO_read_pointer_array(reader, (void **)&sb->keys); if (sb->keys) { - int a; - for (a = 0; a < sb->totkey; a++) { + for (int a = 0; a < sb->totkey; a++) { BLO_read_data_address(reader, &sb->keys[a]); } } @@ -5779,14 +4986,11 @@ static void direct_link_object(BlendDataReader *reader, Object *ob) if (ob->sculpt) { ob->sculpt = NULL; /* Only create data on undo, otherwise rely on editor mode switching. */ - if (reader->fd->memfile && (ob->mode & OB_MODE_ALL_SCULPT)) { + if (BLO_read_data_is_undo(reader) && (ob->mode & OB_MODE_ALL_SCULPT)) { BKE_object_sculpt_data_create(ob); } } - BLO_read_list(reader, &ob->lodlevels); - ob->currentlod = ob->lodlevels.first; - ob->preview = direct_link_preview_image(reader, ob->preview); } @@ -5833,7 +5037,7 @@ static void direct_link_view_layer(BlendDataReader *reader, ViewLayer *view_laye BLO_read_data_address(reader, &view_layer->active_collection); BLO_read_data_address(reader, &view_layer->id_properties); - IDP_DirectLinkGroup_OrFree(&view_layer->id_properties, reader); + IDP_BlendDataRead(reader, &view_layer->id_properties); BLO_read_list(reader, &(view_layer->freestyle_config.modules)); BLO_read_list(reader, &(view_layer->freestyle_config.linesets)); @@ -5853,9 +5057,8 @@ static void lib_link_layer_collection(BlendLibReader *reader, BLO_read_id_address(reader, lib, &layer_collection->collection); } - for (LayerCollection *layer_collection_nested = layer_collection->layer_collections.first; - layer_collection_nested != NULL; - layer_collection_nested = layer_collection_nested->next) { + LISTBASE_FOREACH ( + LayerCollection *, layer_collection_nested, &layer_collection->layer_collections) { lib_link_layer_collection(reader, lib, layer_collection_nested, false); } } @@ -5886,15 +5089,13 @@ static void lib_link_view_layer(BlendLibReader *reader, Library *lib, ViewLayer } } - for (LayerCollection *layer_collection = view_layer->layer_collections.first; - layer_collection != NULL; - layer_collection = layer_collection->next) { + LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) { lib_link_layer_collection(reader, lib, layer_collection, true); } BLO_read_id_address(reader, lib, &view_layer->mat_override); - IDP_LibLinkProperty(view_layer->id_properties, reader); + IDP_BlendReadLib(reader, view_layer->id_properties); } /** \} */ @@ -5955,8 +5156,7 @@ static void direct_link_collection(BlendDataReader *reader, Collection *collecti static void lib_link_collection_data(BlendLibReader *reader, Library *lib, Collection *collection) { - for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) { - cob_next = cob->next; + LISTBASE_FOREACH_MUTABLE (CollectionObject *, cob, &collection->gobject) { BLO_read_id_address(reader, lib, &cob->ob); if (cob->ob == NULL) { @@ -5964,7 +5164,7 @@ static void lib_link_collection_data(BlendLibReader *reader, Library *lib, Colle } } - for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) { + LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { BLO_read_id_address(reader, lib, &child->collection); } } @@ -5993,9 +5193,8 @@ static void lib_link_collection(BlendLibReader *reader, Collection *collection) /* patch for missing scene IDs, can't be in do-versions */ static void composite_patch(bNodeTree *ntree, Scene *scene) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->id == NULL && node->type == CMP_NODE_R_LAYERS) { node->id = &scene->id; } @@ -6020,9 +5219,7 @@ static void link_paint(BlendLibReader *reader, Scene *sce, Paint *p) static void lib_link_sequence_modifiers(BlendLibReader *reader, Scene *scene, ListBase *lb) { - SequenceModifierData *smd; - - for (smd = lb->first; smd; smd = smd->next) { + LISTBASE_FOREACH (SequenceModifierData *, smd, lb) { if (smd->mask_id) { BLO_read_id_address(reader, scene->id.lib, &smd->mask_id); } @@ -6074,7 +5271,7 @@ static void direct_link_view3dshading(BlendDataReader *reader, View3DShading *sh { if (shading->prop) { BLO_read_data_address(reader, &shading->prop); - IDP_DirectLinkGroup_OrFree(&shading->prop, reader); + IDP_BlendDataRead(reader, &shading->prop); } } @@ -6173,10 +5370,7 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce) BLO_read_id_address(reader, sce->id.lib, &sce->toolsettings->gp_sculpt.guide.reference_object); - for (Base *base_legacy_next, *base_legacy = sce->base.first; base_legacy; - base_legacy = base_legacy_next) { - base_legacy_next = base_legacy->next; - + LISTBASE_FOREACH_MUTABLE (Base *, base_legacy, &sce->base) { BLO_read_id_address(reader, sce->id.lib, &base_legacy->object); if (base_legacy->object == NULL) { @@ -6193,8 +5387,8 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce) } Sequence *seq; - SEQ_BEGIN (sce->ed, seq) { - IDP_LibLinkProperty(seq->prop, reader); + SEQ_ALL_BEGIN (sce->ed, seq) { + IDP_BlendReadLib(reader, seq->prop); if (seq->ipo) { BLO_read_id_address( @@ -6235,7 +5429,7 @@ static void lib_link_scene(BlendLibReader *reader, Scene *sce) lib_link_sequence_modifiers(reader, sce, &seq->modifiers); } - SEQ_END; + SEQ_ALL_END; LISTBASE_FOREACH (TimeMarker *, marker, &sce->markers) { if (marker->camera) { @@ -6299,7 +5493,7 @@ static void lib_link_scenes_check_set(Main *bmain) { #ifdef USE_SETSCENE_CHECK const int totscene = BLI_listbase_count(&bmain->scenes); - for (Scene *sce = bmain->scenes.first; sce; sce = sce->id.next) { + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { if (sce->flag & SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK) { sce->flag &= ~SCE_READFILE_LIBLINK_NEED_SETSCENE_CHECK; if (!scene_validate_setscene__liblink(sce, totscene)) { @@ -6316,11 +5510,9 @@ static void lib_link_scenes_check_set(Main *bmain) static void link_recurs_seq(BlendDataReader *reader, ListBase *lb) { - Sequence *seq; - BLO_read_list(reader, lb); - for (seq = lb->first; seq; seq = seq->next) { + LISTBASE_FOREACH (Sequence *, seq, lb) { if (seq->seqbase.first) { link_recurs_seq(reader, &seq->seqbase); } @@ -6365,11 +5557,9 @@ static void direct_link_paint_helper(BlendDataReader *reader, const Scene *scene static void direct_link_sequence_modifiers(BlendDataReader *reader, ListBase *lb) { - SequenceModifierData *smd; - BLO_read_list(reader, lb); - for (smd = lb->first; smd; smd = smd->next) { + LISTBASE_FOREACH (SequenceModifierData *, smd, lb) { if (smd->mask_sequence) { BLO_read_data_address(reader, &smd->mask_sequence); } @@ -6389,13 +5579,6 @@ static void direct_link_sequence_modifiers(BlendDataReader *reader, ListBase *lb static void direct_link_scene(BlendDataReader *reader, Scene *sce) { - Editing *ed; - Sequence *seq; - MetaStack *ms; - RigidBodyWorld *rbw; - ViewLayer *view_layer; - SceneRenderLayer *srl; - sce->depsgraph_hash = NULL; sce->fps_info = NULL; @@ -6410,7 +5593,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) BLO_read_list(reader, &(sce->base)); BLO_read_data_address(reader, &sce->adt); - direct_link_animdata(reader, sce->adt); + BKE_animdata_blend_read_data(reader, sce->adt); BLO_read_list(reader, &sce->keyingsets); direct_link_keyingsets(reader, &sce->keyingsets); @@ -6469,7 +5652,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) ListBase *old_seqbasep = &sce->ed->seqbase; BLO_read_data_address(reader, &sce->ed); - ed = sce->ed; + Editing *ed = sce->ed; BLO_read_data_address(reader, &ed->act_seq); ed->cache = NULL; @@ -6478,7 +5661,8 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) /* recursive link sequences, lb will be correctly initialized */ link_recurs_seq(reader, &ed->seqbase); - SEQ_BEGIN (ed, seq) { + Sequence *seq; + SEQ_ALL_BEGIN (ed, seq) { /* Do as early as possible, so that other parts of reading can rely on valid session UUID. */ BKE_sequence_session_uuid_generate(seq); @@ -6509,7 +5693,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) } BLO_read_data_address(reader, &seq->prop); - IDP_DirectLinkGroup_OrFree(&seq->prop, reader); + IDP_BlendDataRead(reader, &seq->prop); BLO_read_data_address(reader, &seq->strip); if (seq->strip && seq->strip->done == 0) { @@ -6541,7 +5725,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) direct_link_sequence_modifiers(reader, &seq->modifiers); } - SEQ_END; + SEQ_ALL_END; /* link metastack, slight abuse of structs here, * have to restore pointer to internal part in struct */ @@ -6571,7 +5755,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) /* stack */ BLO_read_list(reader, &(ed->metastack)); - for (ms = ed->metastack.first; ms; ms = ms->next) { + LISTBASE_FOREACH (MetaStack *, ms, &ed->metastack) { BLO_read_data_address(reader, &ms->parseq); if (ms->oldbasep == old_seqbasep) { @@ -6603,7 +5787,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) } if (sce->r.ffcodecdata.properties) { BLO_read_data_address(reader, &sce->r.ffcodecdata.properties); - IDP_DirectLinkGroup_OrFree(&sce->r.ffcodecdata.properties, reader); + IDP_BlendDataRead(reader, &sce->r.ffcodecdata.properties); } BLO_read_list(reader, &(sce->markers)); @@ -6611,9 +5795,9 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) BLO_read_list(reader, &(sce->r.layers)); BLO_read_list(reader, &(sce->r.views)); - for (srl = sce->r.layers.first; srl; srl = srl->next) { + LISTBASE_FOREACH (SceneRenderLayer *, srl, &sce->r.layers) { BLO_read_data_address(reader, &srl->prop); - IDP_DirectLinkGroup_OrFree(&srl->prop, reader); + IDP_BlendDataRead(reader, &srl->prop); BLO_read_list(reader, &(srl->freestyleConfig.modules)); BLO_read_list(reader, &(srl->freestyleConfig.linesets)); } @@ -6621,7 +5805,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) direct_link_view_settings(reader, &sce->view_settings); BLO_read_data_address(reader, &sce->rigidbody_world); - rbw = sce->rigidbody_world; + RigidBodyWorld *rbw = sce->rigidbody_world; if (rbw) { BLO_read_data_address(reader, &rbw->shared); @@ -6675,11 +5859,11 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) /* insert into global old-new map for reading without UI (link_global accesses it again) */ link_glob_list(reader->fd, &sce->view_layers); - for (view_layer = sce->view_layers.first; view_layer; view_layer = view_layer->next) { + LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) { direct_link_view_layer(reader, view_layer); } - if (reader->fd->memfile) { + if (BLO_read_data_is_undo(reader)) { /* If it's undo do nothing here, caches are handled by higher-level generic calling code. */ } else { @@ -6694,7 +5878,7 @@ static void direct_link_scene(BlendDataReader *reader, Scene *sce) direct_link_view3dshading(reader, &sce->display.shading); BLO_read_data_address(reader, &sce->layer_properties); - IDP_DirectLinkGroup_OrFree(&sce->layer_properties, reader); + IDP_BlendDataRead(reader, &sce->layer_properties); } /** \} */ @@ -6722,8 +5906,6 @@ static void lib_link_gpencil(BlendLibReader *reader, bGPdata *gpd) /* relinks grease-pencil data - used for direct_link and old file linkage */ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd) { - bGPDpalette *palette; - /* we must firstly have some grease-pencil data to link! */ if (gpd == NULL) { return; @@ -6731,7 +5913,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd) /* relink animdata */ BLO_read_data_address(reader, &gpd->adt); - direct_link_animdata(reader, gpd->adt); + BKE_animdata_blend_read_data(reader, gpd->adt); /* Ensure full objectmode for linked grease pencil. */ if (gpd->id.lib != NULL) { @@ -6751,7 +5933,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd) /* relink palettes (old palettes deprecated, only to convert old files) */ BLO_read_list(reader, &gpd->palettes); if (gpd->palettes.first != NULL) { - for (palette = gpd->palettes.first; palette; palette = palette->next) { + LISTBASE_FOREACH (Palette *, palette, &gpd->palettes) { BLO_read_list(reader, &palette->colors); } } @@ -6786,7 +5968,7 @@ static void direct_link_gpencil(BlendDataReader *reader, bGPdata *gpd) /* relink weight data */ if (gps->dvert) { BLO_read_data_address(reader, &gps->dvert); - direct_link_dverts(reader, gps->totpoints, gps->dvert); + BKE_defvert_blend_read(reader, gps->totpoints, gps->dvert); } } } @@ -6814,19 +5996,17 @@ static void direct_link_panel_list(BlendDataReader *reader, ListBase *lb) static void direct_link_region(BlendDataReader *reader, ARegion *region, int spacetype) { - uiList *ui_list; - direct_link_panel_list(reader, ®ion->panels); BLO_read_list(reader, ®ion->panels_category_active); BLO_read_list(reader, ®ion->ui_lists); - for (ui_list = region->ui_lists.first; ui_list; ui_list = ui_list->next) { + LISTBASE_FOREACH (uiList *, ui_list, ®ion->ui_lists) { ui_list->type = NULL; ui_list->dyn_data = NULL; BLO_read_data_address(reader, &ui_list->properties); - IDP_DirectLinkGroup_OrFree(&ui_list->properties, reader); + IDP_BlendDataRead(reader, &ui_list->properties); } BLO_read_list(reader, ®ion->ui_previews); @@ -6879,9 +6059,6 @@ static void direct_link_region(BlendDataReader *reader, ARegion *region, int spa static void direct_link_area(BlendDataReader *reader, ScrArea *area) { - SpaceLink *sl; - ARegion *region; - BLO_read_list(reader, &(area->spacedata)); BLO_read_list(reader, &(area->regionbase)); @@ -6906,7 +6083,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) area->spacetype = SPACE_EMPTY; } - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { direct_link_region(reader, region, area->spacetype); } @@ -6922,7 +6099,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) blo_do_versions_view3d_split_250(area->spacedata.first, &area->regionbase); } - for (sl = area->spacedata.first; sl; sl = sl->next) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { BLO_read_list(reader, &(sl->regionbase)); /* if we do not have the spacetype registered we cannot @@ -6931,7 +6108,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) sl->spacetype = SPACE_EMPTY; } - for (region = sl->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &sl->regionbase) { direct_link_region(reader, region, sl->spacetype); } @@ -6986,8 +6163,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) space_outliner->treestore = BLI_mempool_create( sizeof(TreeStoreElem), ts->usedelem, 512, BLI_MEMPOOL_ALLOW_ITER); if (ts->usedelem && elems) { - int i; - for (i = 0; i < ts->usedelem; i++) { + for (int i = 0; i < ts->usedelem; i++) { TreeStoreElem *new_elem = BLI_mempool_alloc(space_outliner->treestore); *new_elem = elems[i]; } @@ -7069,7 +6245,6 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) } else if (sl->spacetype == SPACE_CONSOLE) { SpaceConsole *sconsole = (SpaceConsole *)sl; - ConsoleLine *cl, *cl_next; BLO_read_list(reader, &sconsole->scrollback); BLO_read_list(reader, &sconsole->history); @@ -7080,8 +6255,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) /* comma expressions, (e.g. expr1, expr2, expr3) evaluate each expression, * from left to right. the right-most expression sets the result of the comma * expression as a whole*/ - for (cl = sconsole->history.first; cl; cl = cl_next) { - cl_next = cl->next; + LISTBASE_FOREACH_MUTABLE (ConsoleLine *, cl, &sconsole->history) { BLO_read_data_address(reader, &cl->line); if (cl->line) { /* the allocted length is not written, so reset here */ @@ -7243,7 +6417,6 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area) } case SPACE_NODE: { SpaceNode *snode = (SpaceNode *)sl; - bNodeTreePath *path, *path_next; /* node tree can be stored locally in id too, link this first */ BLO_read_id_address(reader, parent_id->lib, &snode->id); @@ -7257,6 +6430,7 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area) BLO_read_id_address(reader, parent_id->lib, &snode->nodetree); } + bNodeTreePath *path; for (path = snode->treepath.first; path; path = path->next) { if (path == snode->treepath.first) { /* first nodetree in path is same as snode->nodetree */ @@ -7272,6 +6446,7 @@ static void lib_link_area(BlendLibReader *reader, ID *parent_id, ScrArea *area) } /* remaining path entries are invalid, remove */ + bNodeTreePath *path_next; for (; path; path = path_next) { path_next = path->next; @@ -7354,12 +6529,10 @@ static void lib_link_wm_xr_data(BlendLibReader *reader, ID *parent_id, wmXrData static void direct_link_windowmanager(BlendDataReader *reader, wmWindowManager *wm) { - wmWindow *win; - id_us_ensure_real(&wm->id); BLO_read_list(reader, &wm->windows); - for (win = wm->windows.first; win; win = win->next) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { BLO_read_data_address(reader, &win->parent); WorkSpaceInstanceHook *hook = win->workspace_hook; @@ -7894,8 +7067,7 @@ void blo_lib_link_restore(Main *oldmain, struct IDNameLib_Map *id_map = BKE_main_idmap_create( newmain, true, oldmain, MAIN_IDMAP_TYPE_NAME); - for (WorkSpace *workspace = newmain->workspaces.first; workspace; - workspace = workspace->id.next) { + LISTBASE_FOREACH (WorkSpace *, workspace, &newmain->workspaces) { LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { lib_link_workspace_layout_restore(id_map, newmain, layout); } @@ -7950,9 +7122,7 @@ void blo_lib_link_restore(Main *oldmain, /* and as patch for 2.48 and older */ void blo_do_versions_view3d_split_250(View3D *v3d, ListBase *regions) { - ARegion *region; - - for (region = regions->first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, regions) { if (region->regiontype == RGN_TYPE_WINDOW && region->regiondata == NULL) { RegionView3D *rv3d; @@ -8057,10 +7227,9 @@ static void lib_link_library(BlendLibReader *UNUSED(reader), Library *UNUSED(lib * in relation to the blend file. */ static void fix_relpaths_library(const char *basepath, Main *main) { - Library *lib; /* BLO_read_from_memory uses a blank filename */ if (basepath == NULL || basepath[0] == '\0') { - for (lib = main->libraries.first; lib; lib = lib->id.next) { + LISTBASE_FOREACH (Library *, lib, &main->libraries) { /* when loading a linked lib into a file which has not been saved, * there is nothing we can be relative to, so instead we need to make * it absolute. This can happen when appending an object with a relative @@ -8072,7 +7241,7 @@ static void fix_relpaths_library(const char *basepath, Main *main) } } else { - for (lib = main->libraries.first; lib; lib = lib->id.next) { + LISTBASE_FOREACH (Library *, lib, &main->libraries) { /* Libraries store both relative and abs paths, recreate relative paths, * relative to the blend file since indirectly linked libs will be * relative to their direct linked library. */ @@ -8098,7 +7267,7 @@ static void lib_link_lightprobe(BlendLibReader *reader, LightProbe *prb) static void direct_link_lightprobe(BlendDataReader *reader, LightProbe *prb) { BLO_read_data_address(reader, &prb->adt); - direct_link_animdata(reader, prb->adt); + BKE_animdata_blend_read_data(reader, prb->adt); } /** \} */ @@ -8115,7 +7284,7 @@ static void lib_link_speaker(BlendLibReader *reader, Speaker *spk) static void direct_link_speaker(BlendDataReader *reader, Speaker *spk) { BLO_read_data_address(reader, &spk->adt); - direct_link_animdata(reader, spk->adt); + BKE_animdata_blend_read_data(reader, spk->adt); #if 0 spk->sound = newdataadr(fd, spk->sound); @@ -8141,7 +7310,7 @@ static void direct_link_sound(BlendDataReader *reader, bSound *sound) sound->cache = NULL; } - if (reader->fd->memfile != NULL) { + if (BLO_read_data_is_undo(reader)) { sound->tags |= SOUND_TAGS_WAVEFORM_NO_RELOAD; } @@ -8175,26 +7344,20 @@ static void direct_link_movieReconstruction(BlendDataReader *reader, static void direct_link_movieTracks(BlendDataReader *reader, ListBase *tracksbase) { - MovieTrackingTrack *track; - BLO_read_list(reader, tracksbase); - for (track = tracksbase->first; track; track = track->next) { + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { BLO_read_data_address(reader, &track->markers); } } static void direct_link_moviePlaneTracks(BlendDataReader *reader, ListBase *plane_tracks_base) { - MovieTrackingPlaneTrack *plane_track; - BLO_read_list(reader, plane_tracks_base); - for (plane_track = plane_tracks_base->first; plane_track; plane_track = plane_track->next) { - int i; - + LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, plane_tracks_base) { BLO_read_pointer_array(reader, (void **)&plane_track->point_tracks); - for (i = 0; i < plane_track->point_tracksnr; i++) { + for (int i = 0; i < plane_track->point_tracksnr; i++) { BLO_read_data_address(reader, &plane_track->point_tracks[i]); } @@ -8205,7 +7368,6 @@ static void direct_link_moviePlaneTracks(BlendDataReader *reader, ListBase *plan static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip) { MovieTracking *tracking = &clip->tracking; - MovieTrackingObject *object; BLO_read_data_address(reader, &clip->adt); @@ -8233,7 +7395,7 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip) BLO_read_list(reader, &tracking->objects); - for (object = tracking->objects.first; object; object = object->next) { + LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) { direct_link_movieTracks(reader, &object->tracks); direct_link_moviePlaneTracks(reader, &object->plane_tracks); direct_link_movieReconstruction(reader, &object->reconstruction); @@ -8242,9 +7404,7 @@ static void direct_link_movieclip(BlendDataReader *reader, MovieClip *clip) static void lib_link_movieTracks(BlendLibReader *reader, MovieClip *clip, ListBase *tracksbase) { - MovieTrackingTrack *track; - - for (track = tracksbase->first; track; track = track->next) { + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracksbase) { BLO_read_id_address(reader, clip->id.lib, &track->gpd); } } @@ -8253,9 +7413,7 @@ static void lib_link_moviePlaneTracks(BlendLibReader *reader, MovieClip *clip, ListBase *tracksbase) { - MovieTrackingPlaneTrack *plane_track; - - for (plane_track = tracksbase->first; plane_track; plane_track = plane_track->next) { + LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, tracksbase) { BLO_read_id_address(reader, clip->id.lib, &plane_track->image); } } @@ -8283,28 +7441,22 @@ static void lib_link_movieclip(BlendLibReader *reader, MovieClip *clip) static void direct_link_mask(BlendDataReader *reader, Mask *mask) { - MaskLayer *masklay; - BLO_read_data_address(reader, &mask->adt); BLO_read_list(reader, &mask->masklayers); - for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { - MaskSpline *spline; - MaskLayerShape *masklay_shape; - + LISTBASE_FOREACH (MaskLayer *, masklay, &mask->masklayers) { /* can't use newdataadr since it's a pointer within an array */ MaskSplinePoint *act_point_search = NULL; BLO_read_list(reader, &masklay->splines); - for (spline = masklay->splines.first; spline; spline = spline->next) { + LISTBASE_FOREACH (MaskSpline *, spline, &masklay->splines) { MaskSplinePoint *points_old = spline->points; - int i; BLO_read_data_address(reader, &spline->points); - for (i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; if (point->tot_uw) { @@ -8321,8 +7473,7 @@ static void direct_link_mask(BlendDataReader *reader, Mask *mask) BLO_read_list(reader, &masklay->splines_shapes); - for (masklay_shape = masklay->splines_shapes.first; masklay_shape; - masklay_shape = masklay_shape->next) { + LISTBASE_FOREACH (MaskLayerShape *, masklay_shape, &masklay->splines_shapes) { BLO_read_data_address(reader, &masklay_shape->data); if (masklay_shape->tot_vert) { @@ -8351,9 +7502,7 @@ static void lib_link_mask(BlendLibReader *reader, Mask *mask) spline = masklay->splines.first; while (spline) { - int i; - - for (i = 0; i < spline->tot_point; i++) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; lib_link_mask_parent(reader, mask, &point->parent); @@ -8374,9 +7523,7 @@ static void lib_link_mask(BlendLibReader *reader, Mask *mask) static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *linestyle) { - LineStyleModifier *m; - - for (m = linestyle->color_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->color_modifiers) { switch (m->type) { case LS_MODIFIER_DISTANCE_FROM_OBJECT: { LineStyleColorModifier_DistanceFromObject *cm = @@ -8386,7 +7533,7 @@ static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *lines } } } - for (m = linestyle->alpha_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->alpha_modifiers) { switch (m->type) { case LS_MODIFIER_DISTANCE_FROM_OBJECT: { LineStyleAlphaModifier_DistanceFromObject *am = @@ -8396,7 +7543,7 @@ static void lib_link_linestyle(BlendLibReader *reader, FreestyleLineStyle *lines } } } - for (m = linestyle->thickness_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->thickness_modifiers) { switch (m->type) { case LS_MODIFIER_DISTANCE_FROM_OBJECT: { LineStyleThicknessModifier_DistanceFromObject *tm = @@ -8582,28 +7729,25 @@ static void direct_link_linestyle_geometry_modifier(BlendDataReader *UNUSED(read static void direct_link_linestyle(BlendDataReader *reader, FreestyleLineStyle *linestyle) { - int a; - LineStyleModifier *modifier; - BLO_read_data_address(reader, &linestyle->adt); - direct_link_animdata(reader, linestyle->adt); + BKE_animdata_blend_read_data(reader, linestyle->adt); BLO_read_list(reader, &linestyle->color_modifiers); - for (modifier = linestyle->color_modifiers.first; modifier; modifier = modifier->next) { + LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->color_modifiers) { direct_link_linestyle_color_modifier(reader, modifier); } BLO_read_list(reader, &linestyle->alpha_modifiers); - for (modifier = linestyle->alpha_modifiers.first; modifier; modifier = modifier->next) { + LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->alpha_modifiers) { direct_link_linestyle_alpha_modifier(reader, modifier); } BLO_read_list(reader, &linestyle->thickness_modifiers); - for (modifier = linestyle->thickness_modifiers.first; modifier; modifier = modifier->next) { + LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->thickness_modifiers) { direct_link_linestyle_thickness_modifier(reader, modifier); } BLO_read_list(reader, &linestyle->geometry_modifiers); - for (modifier = linestyle->geometry_modifiers.first; modifier; modifier = modifier->next) { + LISTBASE_FOREACH (LineStyleModifier *, modifier, &linestyle->geometry_modifiers) { direct_link_linestyle_geometry_modifier(reader, modifier); } - for (a = 0; a < MAX_MTEX; a++) { + for (int a = 0; a < MAX_MTEX; a++) { BLO_read_data_address(reader, &linestyle->mtex[a]); } } @@ -8624,11 +7768,11 @@ static void lib_link_hair(BlendLibReader *reader, Hair *hair) static void direct_link_hair(BlendDataReader *reader, Hair *hair) { BLO_read_data_address(reader, &hair->adt); - direct_link_animdata(reader, hair->adt); + BKE_animdata_blend_read_data(reader, hair->adt); /* Geometry */ - direct_link_customdata(reader, &hair->pdata, hair->totpoint); - direct_link_customdata(reader, &hair->cdata, hair->totcurve); + CustomData_blend_read(reader, &hair->pdata, hair->totpoint); + CustomData_blend_read(reader, &hair->cdata, hair->totcurve); BKE_hair_update_customdata_pointers(hair); /* Materials */ @@ -8651,10 +7795,10 @@ static void lib_link_pointcloud(BlendLibReader *reader, PointCloud *pointcloud) static void direct_link_pointcloud(BlendDataReader *reader, PointCloud *pointcloud) { BLO_read_data_address(reader, &pointcloud->adt); - direct_link_animdata(reader, pointcloud->adt); + BKE_animdata_blend_read_data(reader, pointcloud->adt); /* Geometry */ - direct_link_customdata(reader, &pointcloud->pdata, pointcloud->totpoint); + CustomData_blend_read(reader, &pointcloud->pdata, pointcloud->totpoint); BKE_pointcloud_update_customdata_pointers(pointcloud); /* Materials */ @@ -8682,7 +7826,7 @@ static void lib_link_volume(BlendLibReader *reader, Volume *volume) static void direct_link_volume(BlendDataReader *reader, Volume *volume) { BLO_read_data_address(reader, &volume->adt); - direct_link_animdata(reader, volume->adt); + BKE_animdata_blend_read_data(reader, volume->adt); volume->packedfile = direct_link_packedfile(reader, volume->packedfile); volume->runtime.frame = 0; @@ -8707,7 +7851,7 @@ static void lib_link_simulation(BlendLibReader *reader, Simulation *simulation) static void direct_link_simulation(BlendDataReader *reader, Simulation *simulation) { BLO_read_data_address(reader, &simulation->adt); - direct_link_animdata(reader, simulation->adt); + BKE_animdata_blend_read_data(reader, simulation->adt); BLO_read_list(reader, &simulation->states); LISTBASE_FOREACH (SimulationState *, state, &simulation->states) { @@ -8715,7 +7859,7 @@ static void direct_link_simulation(BlendDataReader *reader, Simulation *simulati BLO_read_data_address(reader, &state->type); if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_SIMULATION)) { ParticleSimulationState *particle_state = (ParticleSimulationState *)state; - direct_link_customdata(reader, &particle_state->attributes, particle_state->tot_particles); + CustomData_blend_read(reader, &particle_state->attributes, particle_state->tot_particles); } } @@ -8753,7 +7897,7 @@ static void placeholders_ensure_valid(Main *bmain) { /* Placeholder ObData IDs won't have any material, we have to update their objects for that, * otherwise the inconsistency between both will lead to crashes (especially in Eevee?). */ - for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { ID *obdata = ob->data; if (obdata != NULL && obdata->tag & LIB_TAG_MISSING) { BKE_object_materials_test(bmain, ob, obdata); @@ -8862,6 +8006,9 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * } const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->blend_read_data != NULL) { + id_type->blend_read_data(&reader, id); + } /* XXX Very weakly handled currently, see comment in read_libblock() before trying to * use it for anything new. */ @@ -8880,9 +8027,6 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * case ID_OB: direct_link_object(&reader, (Object *)id); break; - case ID_ME: - direct_link_mesh(&reader, (Mesh *)id); - break; case ID_CU: direct_link_curve(&reader, (Curve *)id); break; @@ -8913,9 +8057,6 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * case ID_KE: direct_link_key(&reader, (Key *)id); break; - case ID_LT: - direct_link_latt(&reader, (Lattice *)id); - break; case ID_WO: direct_link_world(&reader, (World *)id); break; @@ -8988,6 +8129,10 @@ static bool direct_link_id(FileData *fd, Main *main, const int tag, ID *id, ID * case ID_SIM: direct_link_simulation(&reader, (Simulation *)id); break; + case ID_ME: + case ID_LT: + /* Do nothing. Handled by IDTypeInfo callback. */ + break; } /* try to restore (when undoing) or clear ID's cache pointers. */ @@ -9464,11 +8609,9 @@ static void do_versions_userdef(FileData *fd, BlendFileData *bfd) } if (MAIN_VERSION_OLDER(bmain, 266, 4)) { - bTheme *btheme; - /* Themes for Node and Sequence editor were not using grid color, * but back. we copy this over then. */ - for (btheme = user->themes.first; btheme; btheme = btheme->next) { + LISTBASE_FOREACH (bTheme *, btheme, &user->themes) { copy_v4_v4_uchar(btheme->space_node.grid, btheme->space_node.back); copy_v4_v4_uchar(btheme->space_sequencer.grid, btheme->space_sequencer.back); } @@ -9583,6 +8726,11 @@ static void lib_link_all(FileData *fd, Main *bmain) lib_link_id(&reader, id); + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->blend_read_lib != NULL) { + id_type->blend_read_lib(&reader, id); + } + /* Note: ID types are processed in reverse order as defined by INDEX_ID_XXX enums in DNA_ID.h. * This ensures handling of most dependencies in proper order, as elsewhere in code. * Please keep order of entries in that switch matching that order, it's easier to quickly see @@ -9648,18 +8796,12 @@ static void lib_link_all(FileData *fd, Main *bmain) case ID_LA: lib_link_light(&reader, (Light *)id); break; - case ID_LT: - lib_link_latt(&reader, (Lattice *)id); - break; case ID_MB: lib_link_mball(&reader, (MetaBall *)id); break; case ID_CU: lib_link_curve(&reader, (Curve *)id); break; - case ID_ME: - lib_link_mesh(&reader, (Mesh *)id); - break; case ID_CF: lib_link_cachefiles(&reader, (CacheFile *)id); break; @@ -9713,6 +8855,10 @@ static void lib_link_all(FileData *fd, Main *bmain) case ID_LI: lib_link_library(&reader, (Library *)id); /* Only init users. */ break; + case ID_ME: + case ID_LT: + /* Do nothing. Handled by IDTypeInfo callback. */ + break; } id->tag &= ~LIB_TAG_NEED_LINK; @@ -9749,7 +8895,7 @@ static void lib_link_all(FileData *fd, Main *bmain) static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi) { BLO_read_data_address(reader, &kmi->properties); - IDP_DirectLinkGroup_OrFree(&kmi->properties, reader); + IDP_BlendDataRead(reader, &kmi->properties); kmi->ptr = NULL; kmi->flag &= ~KMI_UPDATE; } @@ -9757,11 +8903,6 @@ static void direct_link_keymapitem(BlendDataReader *reader, wmKeyMapItem *kmi) static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) { UserDef *user; - wmKeyMap *keymap; - wmKeyMapItem *kmi; - wmKeyMapDiffItem *kmdi; - bAddon *addon; - bfd->user = user = read_struct(fd, bhead, "user def"); /* User struct has separate do-version handling */ @@ -9781,7 +8922,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) BLO_read_list(reader, &user->addons); BLO_read_list(reader, &user->autoexec_paths); - for (keymap = user->user_keymaps.first; keymap; keymap = keymap->next) { + LISTBASE_FOREACH (wmKeyMap *, keymap, &user->user_keymaps) { keymap->modal_items = NULL; keymap->poll = NULL; keymap->flag &= ~KEYMAP_UPDATE; @@ -9789,7 +8930,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) BLO_read_list(reader, &keymap->diff_items); BLO_read_list(reader, &keymap->items); - for (kmdi = keymap->diff_items.first; kmdi; kmdi = kmdi->next) { + LISTBASE_FOREACH (wmKeyMapDiffItem *, kmdi, &keymap->diff_items) { BLO_read_data_address(reader, &kmdi->remove_item); BLO_read_data_address(reader, &kmdi->add_item); @@ -9801,14 +8942,14 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) } } - for (kmi = keymap->items.first; kmi; kmi = kmi->next) { + LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { direct_link_keymapitem(reader, kmi); } } LISTBASE_FOREACH (wmKeyConfigPref *, kpt, &user->user_keyconfig_prefs) { BLO_read_data_address(reader, &kpt->prop); - IDP_DirectLinkGroup_OrFree(&kpt->prop, reader); + IDP_BlendDataRead(reader, &kpt->prop); } LISTBASE_FOREACH (bUserMenu *, um, &user->user_menus) { @@ -9817,14 +8958,14 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) if (umi->type == USER_MENU_TYPE_OPERATOR) { bUserMenuItem_Op *umi_op = (bUserMenuItem_Op *)umi; BLO_read_data_address(reader, &umi_op->prop); - IDP_DirectLinkGroup_OrFree(&umi_op->prop, reader); + IDP_BlendDataRead(reader, &umi_op->prop); } } } - for (addon = user->addons.first; addon; addon = addon->next) { + LISTBASE_FOREACH (bAddon *, addon, &user->addons) { BLO_read_data_address(reader, &addon->prop); - IDP_DirectLinkGroup_OrFree(&addon->prop, reader); + IDP_BlendDataRead(reader, &addon->prop); } // XXX @@ -10283,8 +9424,7 @@ static BLOExpandDoitCallback expand_doit; // XXX deprecated - old animation system static void expand_ipo(BlendExpander *expander, Ipo *ipo) { - IpoCurve *icu; - for (icu = ipo->curve.first; icu; icu = icu->next) { + LISTBASE_FOREACH (IpoCurve *, icu, &ipo->curve) { if (icu->driver) { BLO_expand(expander, icu->driver->ob); } @@ -10294,115 +9434,11 @@ static void expand_ipo(BlendExpander *expander, Ipo *ipo) // XXX deprecated - old animation system static void expand_constraint_channels(BlendExpander *expander, ListBase *chanbase) { - bConstraintChannel *chan; - for (chan = chanbase->first; chan; chan = chan->next) { + LISTBASE_FOREACH (bConstraintChannel *, chan, chanbase) { BLO_expand(expander, chan->ipo); } } -static void expand_fmodifiers(BlendExpander *expander, ListBase *list) -{ - FModifier *fcm; - - for (fcm = list->first; fcm; fcm = fcm->next) { - /* library data for specific F-Modifier types */ - switch (fcm->type) { - case FMODIFIER_TYPE_PYTHON: { - FMod_Python *data = (FMod_Python *)fcm->data; - - BLO_expand(expander, data->script); - - break; - } - } - } -} - -static void expand_fcurves(BlendExpander *expander, ListBase *list) -{ - FCurve *fcu; - - for (fcu = list->first; fcu; fcu = fcu->next) { - /* Driver targets if there is a driver */ - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_LOOPER_BEGIN (dvar) { - // TODO: only expand those that are going to get used? - BLO_expand(expander, dtar->id); - } - DRIVER_TARGETS_LOOPER_END; - } - } - - /* F-Curve Modifiers */ - expand_fmodifiers(expander, &fcu->modifiers); - } -} - -static void expand_animdata_nlastrips(BlendExpander *expander, ListBase *list) -{ - NlaStrip *strip; - - for (strip = list->first; strip; strip = strip->next) { - /* check child strips */ - expand_animdata_nlastrips(expander, &strip->strips); - - /* check F-Curves */ - expand_fcurves(expander, &strip->fcurves); - - /* check F-Modifiers */ - expand_fmodifiers(expander, &strip->modifiers); - - /* relink referenced action */ - BLO_expand(expander, strip->act); - } -} - -static void expand_animdata(BlendExpander *expander, AnimData *adt) -{ - NlaTrack *nlt; - - /* own action */ - BLO_expand(expander, adt->action); - BLO_expand(expander, adt->tmpact); - - /* drivers - assume that these F-Curves have driver data to be in this list... */ - expand_fcurves(expander, &adt->drivers); - - /* nla-data - referenced actions */ - for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) { - expand_animdata_nlastrips(expander, &nlt->strips); - } -} - -static void expand_idprops(BlendExpander *expander, IDProperty *prop) -{ - if (!prop) { - return; - } - - switch (prop->type) { - case IDP_ID: - BLO_expand(expander, IDP_Id(prop)); - break; - case IDP_IDPARRAY: { - IDProperty *idp_array = IDP_IDPArray(prop); - for (int i = 0; i < prop->len; i++) { - expand_idprops(expander, &idp_array[i]); - } - break; - } - case IDP_GROUP: - LISTBASE_FOREACH (IDProperty *, loop, &prop->data.group) { - expand_idprops(expander, loop); - } - break; - } -} - static void expand_id(BlendExpander *expander, ID *id); static void expand_nodetree(BlendExpander *expander, bNodeTree *ntree); static void expand_collection(BlendExpander *expander, Collection *collection); @@ -10427,7 +9463,7 @@ static void expand_id_embedded_id(BlendExpander *expander, ID *id) static void expand_id(BlendExpander *expander, ID *id) { - expand_idprops(expander, id->properties); + IDP_BlendReadExpand(expander, id->properties); if (id->override_library) { BLO_expand(expander, id->override_library->reference); @@ -10436,7 +9472,7 @@ static void expand_id(BlendExpander *expander, ID *id) AnimData *adt = BKE_animdata_from_id(id); if (adt != NULL) { - expand_animdata(expander, adt); + BKE_animdata_blend_read_expand(expander, adt); } expand_id_embedded_id(expander, id); @@ -10444,17 +9480,15 @@ static void expand_id(BlendExpander *expander, ID *id) static void expand_action(BlendExpander *expander, bAction *act) { - bActionChannel *chan; - // XXX deprecated - old animation system -------------- - for (chan = act->chanbase.first; chan; chan = chan->next) { + LISTBASE_FOREACH (bActionChannel *, chan, &act->chanbase) { BLO_expand(expander, chan->ipo); expand_constraint_channels(expander, &chan->constraintChannels); } // --------------------------------------------------- /* F-Curves in Action */ - expand_fcurves(expander, &act->curves); + BKE_fcurve_blend_read_expand(expander, &act->curves); LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { if (marker->camera) { @@ -10465,12 +9499,9 @@ static void expand_action(BlendExpander *expander, bAction *act) static void expand_keyingsets(BlendExpander *expander, ListBase *list) { - KeyingSet *ks; - KS_Path *ksp; - /* expand the ID-pointers in KeyingSets's paths */ - for (ks = list->first; ks; ks = ks->next) { - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + LISTBASE_FOREACH (KeyingSet *, ks, list) { + LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { BLO_expand(expander, ksp->id); } } @@ -10478,15 +9509,13 @@ static void expand_keyingsets(BlendExpander *expander, ListBase *list) static void expand_particlesettings(BlendExpander *expander, ParticleSettings *part) { - int a; - BLO_expand(expander, part->instance_object); BLO_expand(expander, part->instance_collection); BLO_expand(expander, part->force_group); BLO_expand(expander, part->bb_ob); BLO_expand(expander, part->collision_group); - for (a = 0; a < MAX_MTEX; a++) { + for (int a = 0; a < MAX_MTEX; a++) { if (part->mtex[a]) { BLO_expand(expander, part->mtex[a]->tex); BLO_expand(expander, part->mtex[a]->object); @@ -10507,11 +9536,8 @@ static void expand_particlesettings(BlendExpander *expander, ParticleSettings *p } if (part->boids) { - BoidState *state; - BoidRule *rule; - - for (state = part->boids->states.first; state; state = state->next) { - for (rule = state->rules.first; rule; rule = rule->next) { + LISTBASE_FOREACH (BoidState *, state, &part->boids->states) { + LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { if (rule->type == eBoidRuleType_Avoid) { BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; BLO_expand(expander, gabr->ob); @@ -10553,7 +9579,7 @@ static void expand_key(BlendExpander *expander, Key *key) static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock) { - expand_idprops(expander, sock->prop); + IDP_BlendReadExpand(expander, sock->prop); if (sock->default_value != NULL) { @@ -10595,18 +9621,16 @@ static void expand_node_sockets(BlendExpander *expander, ListBase *sockets) static void expand_nodetree(BlendExpander *expander, bNodeTree *ntree) { - bNode *node; - if (ntree->gpd) { BLO_expand(expander, ntree->gpd); } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->id && node->type != CMP_NODE_R_LAYERS) { BLO_expand(expander, node->id); } - expand_idprops(expander, node->prop); + IDP_BlendReadExpand(expander, node->prop); expand_node_sockets(expander, &node->inputs); expand_node_sockets(expander, &node->outputs); @@ -10649,12 +9673,6 @@ static void expand_light(BlendExpander *expander, Light *la) BLO_expand(expander, la->ipo); // XXX deprecated - old animation system } -static void expand_lattice(BlendExpander *expander, Lattice *lt) -{ - BLO_expand(expander, lt->ipo); // XXX deprecated - old animation system - BLO_expand(expander, lt->key); -} - static void expand_world(BlendExpander *expander, World *wrld) { BLO_expand(expander, wrld->ipo); // XXX deprecated - old animation system @@ -10662,18 +9680,14 @@ static void expand_world(BlendExpander *expander, World *wrld) static void expand_mball(BlendExpander *expander, MetaBall *mb) { - int a; - - for (a = 0; a < mb->totcol; a++) { + for (int a = 0; a < mb->totcol; a++) { BLO_expand(expander, mb->mat[a]); } } static void expand_curve(BlendExpander *expander, Curve *cu) { - int a; - - for (a = 0; a < cu->totcol; a++) { + for (int a = 0; a < cu->totcol; a++) { BLO_expand(expander, cu->mat[a]); } @@ -10688,18 +9702,6 @@ static void expand_curve(BlendExpander *expander, Curve *cu) BLO_expand(expander, cu->textoncurve); } -static void expand_mesh(BlendExpander *expander, Mesh *me) -{ - int a; - - for (a = 0; a < me->totcol; a++) { - BLO_expand(expander, me->mat[a]); - } - - BLO_expand(expander, me->key); - BLO_expand(expander, me->texcomesh); -} - /* callback function used to expand constraint ID-links */ static void expand_constraint_cb(bConstraint *UNUSED(con), ID **idpoin, @@ -10712,12 +9714,10 @@ static void expand_constraint_cb(bConstraint *UNUSED(con), static void expand_constraints(BlendExpander *expander, ListBase *lb) { - bConstraint *curcon; - BKE_constraints_id_loop(lb, expand_constraint_cb, expander); /* deprecated manual expansion stuff */ - for (curcon = lb->first; curcon; curcon = curcon->next) { + LISTBASE_FOREACH (bConstraint *, curcon, lb) { if (curcon->ipo) { BLO_expand(expander, curcon->ipo); // XXX deprecated - old animation system } @@ -10726,22 +9726,20 @@ static void expand_constraints(BlendExpander *expander, ListBase *lb) static void expand_pose(BlendExpander *expander, bPose *pose) { - bPoseChannel *chan; - if (!pose) { return; } - for (chan = pose->chanbase.first; chan; chan = chan->next) { + LISTBASE_FOREACH (bPoseChannel *, chan, &pose->chanbase) { expand_constraints(expander, &chan->constraints); - expand_idprops(expander, chan->prop); + IDP_BlendReadExpand(expander, chan->prop); BLO_expand(expander, chan->custom); } } static void expand_bones(BlendExpander *expander, Bone *bone) { - expand_idprops(expander, bone->prop); + IDP_BlendReadExpand(expander, bone->prop); LISTBASE_FOREACH (Bone *, curBone, &bone->childbase) { expand_bones(expander, curBone); @@ -10766,11 +9764,6 @@ static void expand_object_expandModifiers(void *userData, static void expand_object(BlendExpander *expander, Object *ob) { - ParticleSystem *psys; - bActionStrip *strip; - PartEff *paf; - int a; - BLO_expand(expander, ob->data); /* expand_object_expandModifier() */ @@ -10800,18 +9793,18 @@ static void expand_object(BlendExpander *expander, Object *ob) expand_constraint_channels(expander, &ob->constraintChannels); - for (strip = ob->nlastrips.first; strip; strip = strip->next) { + LISTBASE_FOREACH (bActionStrip *, strip, &ob->nlastrips) { BLO_expand(expander, strip->object); BLO_expand(expander, strip->act); BLO_expand(expander, strip->ipo); } // XXX deprecated - old animation system (for version patching only) - for (a = 0; a < ob->totcol; a++) { + for (int a = 0; a < ob->totcol; a++) { BLO_expand(expander, ob->mat[a]); } - paf = blo_do_version_give_parteff_245(ob); + PartEff *paf = blo_do_version_give_parteff_245(ob); if (paf && paf->group) { BLO_expand(expander, paf->group); } @@ -10827,7 +9820,7 @@ static void expand_object(BlendExpander *expander, Object *ob) BLO_expand(expander, ob->proxy_group); } - for (psys = ob->particlesystem.first; psys; psys = psys->next) { + LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { BLO_expand(expander, psys->part); } @@ -10848,13 +9841,6 @@ static void expand_object(BlendExpander *expander, Object *ob) BLO_expand(expander, ob->rigidbody_constraint->ob1); BLO_expand(expander, ob->rigidbody_constraint->ob2); } - - if (ob->currentlod) { - LodLevel *level; - for (level = ob->lodlevels.first; level; level = level->next) { - BLO_expand(expander, level->source); - } - } } #ifdef USE_COLLECTION_COMPAT_28 @@ -10872,10 +9858,6 @@ static void expand_scene_collection(BlendExpander *expander, SceneCollection *sc static void expand_scene(BlendExpander *expander, Scene *sce) { - SceneRenderLayer *srl; - FreestyleModuleConfig *module; - FreestyleLineSet *lineset; - LISTBASE_FOREACH (Base *, base_legacy, &sce->base) { BLO_expand(expander, base_legacy->object); } @@ -10888,14 +9870,14 @@ static void expand_scene(BlendExpander *expander, Scene *sce) BLO_expand(expander, sce->set); } - for (srl = sce->r.layers.first; srl; srl = srl->next) { + LISTBASE_FOREACH (SceneRenderLayer *, srl, &sce->r.layers) { BLO_expand(expander, srl->mat_override); - for (module = srl->freestyleConfig.modules.first; module; module = module->next) { + LISTBASE_FOREACH (FreestyleModuleConfig *, module, &srl->freestyleConfig.modules) { if (module->script) { BLO_expand(expander, module->script); } } - for (lineset = srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) { + LISTBASE_FOREACH (FreestyleLineSet *, lineset, &srl->freestyleConfig.linesets) { if (lineset->group) { BLO_expand(expander, lineset->group); } @@ -10904,15 +9886,15 @@ static void expand_scene(BlendExpander *expander, Scene *sce) } LISTBASE_FOREACH (ViewLayer *, view_layer, &sce->view_layers) { - expand_idprops(expander, view_layer->id_properties); + IDP_BlendReadExpand(expander, view_layer->id_properties); - for (module = view_layer->freestyle_config.modules.first; module; module = module->next) { + LISTBASE_FOREACH (FreestyleModuleConfig *, module, &view_layer->freestyle_config.modules) { if (module->script) { BLO_expand(expander, module->script); } } - for (lineset = view_layer->freestyle_config.linesets.first; lineset; lineset = lineset->next) { + LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer->freestyle_config.linesets) { if (lineset->group) { BLO_expand(expander, lineset->group); } @@ -10927,8 +9909,8 @@ static void expand_scene(BlendExpander *expander, Scene *sce) if (sce->ed) { Sequence *seq; - SEQ_BEGIN (sce->ed, seq) { - expand_idprops(expander, seq->prop); + SEQ_ALL_BEGIN (sce->ed, seq) { + IDP_BlendReadExpand(expander, seq->prop); if (seq->scene) { BLO_expand(expander, seq->scene); @@ -10951,7 +9933,7 @@ static void expand_scene(BlendExpander *expander, Scene *sce) BLO_expand(expander, data->text_font); } } - SEQ_END; + SEQ_ALL_END; } if (sce->rigidbody_world) { @@ -11023,17 +10005,10 @@ static void expand_mask_parent(BlendExpander *expander, MaskParent *parent) static void expand_mask(BlendExpander *expander, Mask *mask) { - MaskLayer *mask_layer; - - for (mask_layer = mask->masklayers.first; mask_layer; mask_layer = mask_layer->next) { - MaskSpline *spline; - - for (spline = mask_layer->splines.first; spline; spline = spline->next) { - int i; - - for (i = 0; i < spline->tot_point; i++) { + LISTBASE_FOREACH (MaskLayer *, mask_layer, &mask->masklayers) { + LISTBASE_FOREACH (MaskSpline *, spline, &mask_layer->splines) { + for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; - expand_mask_parent(expander, &point->parent); } @@ -11044,27 +10019,24 @@ static void expand_mask(BlendExpander *expander, Mask *mask) static void expand_linestyle(BlendExpander *expander, FreestyleLineStyle *linestyle) { - int a; - LineStyleModifier *m; - - for (a = 0; a < MAX_MTEX; a++) { + for (int a = 0; a < MAX_MTEX; a++) { if (linestyle->mtex[a]) { BLO_expand(expander, linestyle->mtex[a]->tex); BLO_expand(expander, linestyle->mtex[a]->object); } } - for (m = linestyle->color_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->color_modifiers) { if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { BLO_expand(expander, ((LineStyleColorModifier_DistanceFromObject *)m)->target); } } - for (m = linestyle->alpha_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->alpha_modifiers) { if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { BLO_expand(expander, ((LineStyleAlphaModifier_DistanceFromObject *)m)->target); } } - for (m = linestyle->thickness_modifiers.first; m; m = m->next) { + LISTBASE_FOREACH (LineStyleModifier *, m, &linestyle->thickness_modifiers) { if (m->type == LS_MODIFIER_DISTANCE_FROM_OBJECT) { BLO_expand(expander, ((LineStyleThicknessModifier_DistanceFromObject *)m)->target); } @@ -11094,10 +10066,6 @@ static void expand_hair(BlendExpander *expander, Hair *hair) for (int a = 0; a < hair->totcol; a++) { BLO_expand(expander, hair->mat[a]); } - - if (hair->adt) { - expand_animdata(expander, hair->adt); - } } static void expand_pointcloud(BlendExpander *expander, PointCloud *pointcloud) @@ -11105,10 +10073,6 @@ static void expand_pointcloud(BlendExpander *expander, PointCloud *pointcloud) for (int a = 0; a < pointcloud->totcol; a++) { BLO_expand(expander, pointcloud->mat[a]); } - - if (pointcloud->adt) { - expand_animdata(expander, pointcloud->adt); - } } static void expand_volume(BlendExpander *expander, Volume *volume) @@ -11116,17 +10080,10 @@ static void expand_volume(BlendExpander *expander, Volume *volume) for (int a = 0; a < volume->totcol; a++) { BLO_expand(expander, volume->mat[a]); } - - if (volume->adt) { - expand_animdata(expander, volume->adt); - } } static void expand_simulation(BlendExpander *expander, Simulation *simulation) { - if (simulation->adt) { - expand_animdata(expander, simulation->adt); - } LISTBASE_FOREACH (SimulationDependency *, dependency, &simulation->dependencies) { BLO_expand(expander, dependency->id); } @@ -11169,13 +10126,15 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) if (id->tag & LIB_TAG_NEED_EXPAND) { expand_id(&expander, id); + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->blend_read_expand != NULL) { + id_type->blend_read_expand(&expander, id); + } + switch (GS(id->name)) { case ID_OB: expand_object(&expander, (Object *)id); break; - case ID_ME: - expand_mesh(&expander, (Mesh *)id); - break; case ID_CU: expand_curve(&expander, (Curve *)id); break; @@ -11194,9 +10153,6 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) case ID_WO: expand_world(&expander, (World *)id); break; - case ID_LT: - expand_lattice(&expander, (Lattice *)id); - break; case ID_LA: expand_light(&expander, (Light *)id); break; @@ -11287,9 +10243,7 @@ void BLO_expand_main(void *fdhandle, Main *mainvar) static bool object_in_any_scene(Main *bmain, Object *ob) { - Scene *sce; - - for (sce = bmain->scenes.first; sce; sce = sce->id.next) { + LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) { if (BKE_scene_object_find(sce, ob)) { return true; } @@ -11300,9 +10254,7 @@ static bool object_in_any_scene(Main *bmain, Object *ob) static bool object_in_any_collection(Main *bmain, Object *ob) { - Collection *collection; - - for (collection = bmain->collections.first; collection; collection = collection->id.next) { + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (BKE_collection_has_object(collection, ob)) { return true; } @@ -11326,7 +10278,7 @@ static void add_loose_objects_to_scene(Main *mainvar, /* Give all objects which are LIB_TAG_INDIRECT a base, * or for a collection when *lib has been set. */ - for (Object *ob = mainvar->objects.first; ob; ob = ob->id.next) { + LISTBASE_FOREACH (Object *, ob, &mainvar->objects) { bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0; if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { if (do_append) { @@ -11393,8 +10345,7 @@ static void add_collections_to_scene(Main *mainvar, } /* Give all objects which are tagged a base. */ - for (Collection *collection = mainvar->collections.first; collection; - collection = collection->id.next) { + LISTBASE_FOREACH (Collection *, collection, &mainvar->collections) { if ((flag & FILE_GROUP_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) { /* Any indirect collection should not have been tagged. */ BLI_assert((collection->id.tag & LIB_TAG_INDIRECT) == 0); @@ -11437,8 +10388,7 @@ static void add_collections_to_scene(Main *mainvar, * Note that we only check object directly into that collection, * not recursively into its children. */ - for (CollectionObject *coll_ob = collection->gobject.first; coll_ob != NULL; - coll_ob = coll_ob->next) { + LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; if ((ob->id.tag & (LIB_TAG_PRE_EXISTING | LIB_TAG_DOIT | LIB_TAG_INDIRECT)) == 0 && (ob->id.lib == lib) && (object_in_any_scene(bmain, ob) == 0)) { @@ -11452,8 +10402,7 @@ static void add_collections_to_scene(Main *mainvar, BKE_collection_child_add(bmain, active_collection, collection); if (flag & FILE_AUTOSELECT) { - for (CollectionObject *coll_ob = collection->gobject.first; coll_ob != NULL; - coll_ob = coll_ob->next) { + LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; Base *base = BKE_view_layer_base_find(view_layer, ob); if (base) { @@ -11679,9 +10628,7 @@ static void split_main_newid(Main *mainptr, Main *main_newid) while (i--) { BLI_listbase_clear(lbarray_newid[i]); - for (ID *id = lbarray[i]->first, *idnext; id; id = idnext) { - idnext = id->next; - + LISTBASE_FOREACH_MUTABLE (ID *, id, lbarray[i]) { if (id->tag & LIB_TAG_NEW) { BLI_remlink(lbarray[i], id); BLI_addtail(lbarray_newid[i], id); @@ -12127,8 +11074,8 @@ static void read_libraries(FileData *basefd, ListBase *mainlist) } /* Note: No need to call #do_versions_after_linking() or #BKE_main_id_refcount_recompute() - * here, as this function is only called for library 'subset' data handling, as part of either - * full blendfile reading (#blo_read_file_internal()), or library-data linking + * here, as this function is only called for library 'subset' data handling, as part of + * either full blendfile reading (#blo_read_file_internal()), or library-data linking * (#library_link_end()). */ /* Free file data we no longer need. */ @@ -12145,6 +11092,11 @@ void *BLO_read_get_new_data_address(BlendDataReader *reader, const void *old_add return newdataadr(reader->fd, old_address); } +void *BLO_read_get_new_packed_address(BlendDataReader *reader, const void *old_address) +{ + return newpackedadr(reader->fd, old_address); +} + ID *BLO_read_get_new_id_address(BlendLibReader *reader, Library *lib, ID *id) { return newlibadr(reader->fd, lib, id); @@ -12300,6 +11252,16 @@ void BLO_read_pointer_array(BlendDataReader *reader, void **ptr_p) *ptr_p = final_array; } +bool BLO_read_data_is_undo(BlendDataReader *reader) +{ + return reader->fd->memfile != NULL; +} + +bool BLO_read_lib_is_undo(BlendLibReader *reader) +{ + return reader->fd->memfile != NULL; +} + void BLO_expand_id(BlendExpander *expander, ID *id) { expand_doit(expander->fd, expander->main, id); diff --git a/source/blender/blenloader/intern/versioning_250.c b/source/blender/blenloader/intern/versioning_250.c index 0e753c84476..dad86f80813 100644 --- a/source/blender/blenloader/intern/versioning_250.c +++ b/source/blender/blenloader/intern/versioning_250.c @@ -666,7 +666,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { if (scene->ed && scene->ed->seqbasep) { - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->type == SEQ_TYPE_SOUND_HD) { char str[FILE_MAX]; BLI_join_dirfile(str, sizeof(str), seq->strip->dir, seq->strip->stripdata->name); @@ -682,7 +682,7 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) #undef SEQ_USE_PROXY_CUSTOM_DIR #undef SEQ_USE_PROXY_CUSTOM_FILE } - SEQ_END; + SEQ_ALL_END; } } @@ -1409,10 +1409,10 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) sce->r.ffcodecdata.audio_codec = 0x0; // CODEC_ID_NONE } - SEQ_BEGIN (sce->ed, seq) { + SEQ_ALL_BEGIN (sce->ed, seq) { seq->volume = 1.0f; } - SEQ_END; + SEQ_ALL_END; } /* particle brush strength factor was changed from int to float */ @@ -1681,12 +1681,12 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->sat == 0.0f) { seq->sat = 1.0f; } } - SEQ_END; + SEQ_ALL_END; } /* GSOC 2010 Sculpt - New settings for Brush */ @@ -2166,10 +2166,10 @@ void blo_do_versions_250(FileData *fd, Library *lib, Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { scene->r.ffcodecdata.audio_channels = 2; scene->audio.volume = 1.0f; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { seq->pitch = 1.0f; } - SEQ_END; + SEQ_ALL_END; } } diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index 1ac90398c0a..dec5c8d495a 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1489,7 +1489,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) if (scene->ed) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { Strip *strip = seq->strip; if (strip && strip->color_balance) { @@ -1512,7 +1512,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) strip->color_balance = NULL; } } - SEQ_END; + SEQ_ALL_END; } } } @@ -1804,7 +1804,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { enum { SEQ_MAKE_PREMUL = (1 << 6) }; if (seq->flag & SEQ_MAKE_PREMUL) { seq->alpha_mode = SEQ_ALPHA_STRAIGHT; @@ -1813,7 +1813,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) BKE_sequence_alpha_mode_from_extension(seq); } } - SEQ_END; + SEQ_ALL_END; if (scene->r.bake_samples == 0) { scene->r.bake_samples = 256; @@ -2447,13 +2447,13 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->type == SEQ_TYPE_WIPE) { WipeVars *wv = seq->effectdata; wv->angle = DEG2RADF(wv->angle); } } - SEQ_END; + SEQ_ALL_END; } FOREACH_NODETREE_BEGIN (bmain, ntree, id) { diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index b19c6221391..2452ac43bd4 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -891,17 +891,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - - /* hysteresis set to 10% but not activated */ - if (!DNA_struct_elem_find(fd->filesdna, "LodLevel", "int", "obhysteresis")) { - Object *ob; - for (ob = bmain->objects.first; ob; ob = ob->id.next) { - LodLevel *level; - for (level = ob->lodlevels.first; level; level = level->next) { - level->obhysteresis = 10; - } - } - } } if (!MAIN_VERSION_ATLEAST(bmain, 274, 4)) { @@ -924,7 +913,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) srv = scene->r.views.last; BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix)); - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format"); #define SEQ_USE_PROXY_CUSTOM_DIR (1 << 19) @@ -940,7 +929,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) #undef SEQ_USE_PROXY_CUSTOM_DIR #undef SEQ_USE_PROXY_CUSTOM_FILE } - SEQ_END; + SEQ_ALL_END; } for (screen = bmain->screens.first; screen; screen = screen->id.next) { @@ -1225,7 +1214,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->type != SEQ_TYPE_TEXT) { continue; } @@ -1241,7 +1230,7 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) data->shadow_color[3] = 1.0f; } } - SEQ_END; + SEQ_ALL_END; } /* Adding "Properties" region to DopeSheet */ diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 0d8f2eac99e..2434d9e9f74 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -3463,7 +3463,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (scene->ed) { Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { seq->flag &= ~(SEQ_FLAG_UNUSED_6 | SEQ_FLAG_UNUSED_18 | SEQ_FLAG_UNUSED_19 | SEQ_FLAG_UNUSED_21); if (seq->type == SEQ_TYPE_SPEED) { @@ -3471,7 +3471,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) s->flags &= ~(SEQ_SPEED_UNUSED_1); } } - SEQ_END; + SEQ_ALL_END; } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index bc13e3b3a39..aa222616e4c 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -42,6 +42,7 @@ #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "BKE_softbody.h" #include "BLO_readfile.h" #include "readfile.h" @@ -208,6 +209,17 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) } } + if (!MAIN_VERSION_ATLEAST(bmain, 291, 1)) { + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (BKE_collection_cycles_fix(bmain, collection)) { + printf( + "WARNING: Cycle detected in collection '%s', fixed as best as possible.\n" + "You may have to reconstruct your View Layers...\n", + collection->id.name); + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -219,18 +231,26 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) * \note Keep this message at the bottom of the function. */ { - LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { - if (BKE_collection_cycles_fix(bmain, collection)) { - printf( - "WARNING: Cycle detected in collection '%s', fixed as best as possible.\n" - "You may have to reconstruct your View Layers...\n", - collection->id.name); - } - } + /* Keep this block, even when empty. */ } } +static void panels_remove_x_closed_flag_recursive(Panel *panel) +{ + const bool was_closed_x = panel->flag & PNL_UNUSED_1; + const bool was_closed_y = panel->flag & PNL_CLOSED; /* That value was the Y closed flag. */ + + SET_FLAG_FROM_TEST(panel->flag, was_closed_x || was_closed_y, PNL_CLOSED); + + /* Clear the old PNL_CLOSEDX flag. */ + panel->flag &= ~PNL_UNUSED_1; + + LISTBASE_FOREACH (Panel *, child_panel, &panel->children) { + panels_remove_x_closed_flag_recursive(child_panel); + } +} + void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) { UNUSED_VARS(fd); @@ -409,17 +429,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #BLO_version_defaults_userpref_blend - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + if (!MAIN_VERSION_ATLEAST(bmain, 291, 1)) { /* Initialize additional parameter of the Nishita sky model and change altitude unit. */ if (!DNA_struct_elem_find(fd->filesdna, "NodeTexSky", "float", "sun_intensity")) { @@ -484,5 +494,49 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + /* Remove panel X axis collapsing, a remnant of horizontal panel alignment. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { + panels_remove_x_closed_flag_recursive(panel); + } + } + } + } + + /* Initialize solver for Boolean. */ + if (!DNA_struct_elem_find(fd->filesdna, "BooleanModifierData", "enum", "solver")) { + for (Object *object = bmain->objects.first; object != NULL; object = object->id.next) { + LISTBASE_FOREACH (ModifierData *, md, &object->modifiers) { + if (md->type == eModifierType_Boolean) { + BooleanModifierData *bmd = (BooleanModifierData *)md; + bmd->solver = eBooleanModifierSolver_Fast; + } + } + } + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "SoftBody", "int", "solver_mode")) { + for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) { + if (ob->soft != NULL) { + sbExternalSetDefault(ob->soft); + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #BLO_version_defaults_userpref_blend + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 88ccb551e16..58d57f12b8f 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -1251,12 +1251,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) while (sce) { ed = sce->ed; if (ed) { - SEQ_BEGIN (sce->ed, seq) { + SEQ_ALL_BEGIN (sce->ed, seq) { if (seq->type == SEQ_TYPE_IMAGE || seq->type == SEQ_TYPE_MOVIE) { seq->alpha_mode = SEQ_ALPHA_STRAIGHT; } } - SEQ_END; + SEQ_ALL_END; } sce = sce->id.next; @@ -2442,12 +2442,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) Sequence *seq; for (sce = bmain->scenes.first; sce; sce = sce->id.next) { - SEQ_BEGIN (sce->ed, seq) { + SEQ_ALL_BEGIN (sce->ed, seq) { if (seq->blend_mode == 0) { seq->blend_opacity = 100.0f; } } - SEQ_END; + SEQ_ALL_END; } } @@ -2595,12 +2595,12 @@ void blo_do_versions_pre250(FileData *fd, Library *lib, Main *bmain) while (sce) { ed = sce->ed; if (ed) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->strip && seq->strip->proxy) { seq->strip->proxy->quality = 90; } } - SEQ_END; + SEQ_CURRENT_END; } sce = sce->id.next; diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index 0b116804481..d04907872b7 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -228,6 +228,11 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) */ { /* Keep this block, even when empty. */ + + /* The new defaults for the file browser theme are the same as + * the outliner's, and it's less disruptive to just copy them. */ + copy_v4_v4_uchar(btheme->space_file.back, btheme->space_outliner.back); + copy_v4_v4_uchar(btheme->space_file.row_alternate, btheme->space_outliner.row_alternate); } #undef FROM_DEFAULT_V4_UCHAR @@ -758,6 +763,12 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) userdef->statusbar_flag = STATUSBAR_SHOW_VERSION; } + if (!USER_VERSION_ATLEAST(291, 1)) { + if (userdef->collection_instance_empty_size == 0) { + userdef->collection_instance_empty_size = 1.0f; + } + } + /** * Versioning code until next subversion bump goes here. * @@ -769,10 +780,6 @@ void BLO_version_defaults_userpref_blend(Main *bmain, UserDef *userdef) */ { /* Keep this block, even when empty. */ - - if (userdef->collection_instance_empty_size == 0) { - userdef->collection_instance_empty_size = 1.0f; - } } if (userdef->pixelsize == 0.0f) { diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index b6f0d5bd70b..3f968b513bb 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -152,6 +152,7 @@ #include "MEM_guardedalloc.h" // MEM_freeN #include "BKE_action.h" +#include "BKE_anim_data.h" #include "BKE_armature.h" #include "BKE_blender_version.h" #include "BKE_bpath.h" @@ -160,10 +161,12 @@ #include "BKE_constraint.h" #include "BKE_curve.h" #include "BKE_curveprofile.h" +#include "BKE_deform.h" #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" #include "BKE_global.h" // for G #include "BKE_gpencil_modifier.h" +#include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -656,108 +659,6 @@ static void writelist_id(WriteData *wd, int filecode, const char *structname, co * These functions are used by blender's .blend system for file saving/loading. * \{ */ -void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer); -void IDP_WriteProperty(const IDProperty *prop, BlendWriter *writer); - -static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer) -{ - /*REMEMBER to set totalen to len in the linking code!!*/ - if (prop->data.pointer) { - BLO_write_raw(writer, MEM_allocN_len(prop->data.pointer), prop->data.pointer); - - if (prop->subtype == IDP_GROUP) { - IDProperty **array = prop->data.pointer; - int a; - - for (a = 0; a < prop->len; a++) { - IDP_WriteProperty(array[a], writer); - } - } - } -} - -static void IDP_WriteIDPArray(const IDProperty *prop, BlendWriter *writer) -{ - /*REMEMBER to set totalen to len in the linking code!!*/ - if (prop->data.pointer) { - const IDProperty *array = prop->data.pointer; - int a; - - BLO_write_struct_array(writer, IDProperty, prop->len, array); - - for (a = 0; a < prop->len; a++) { - IDP_WriteProperty_OnlyData(&array[a], writer); - } - } -} - -static void IDP_WriteString(const IDProperty *prop, BlendWriter *writer) -{ - /*REMEMBER to set totalen to len in the linking code!!*/ - BLO_write_raw(writer, prop->len, prop->data.pointer); -} - -static void IDP_WriteGroup(const IDProperty *prop, BlendWriter *writer) -{ - IDProperty *loop; - - for (loop = prop->data.group.first; loop; loop = loop->next) { - IDP_WriteProperty(loop, writer); - } -} - -/* Functions to read/write ID Properties */ -void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer) -{ - switch (prop->type) { - case IDP_GROUP: - IDP_WriteGroup(prop, writer); - break; - case IDP_STRING: - IDP_WriteString(prop, writer); - break; - case IDP_ARRAY: - IDP_WriteArray(prop, writer); - break; - case IDP_IDPARRAY: - IDP_WriteIDPArray(prop, writer); - break; - } -} - -void IDP_WriteProperty(const IDProperty *prop, BlendWriter *writer) -{ - BLO_write_struct(writer, IDProperty, prop); - IDP_WriteProperty_OnlyData(prop, writer); -} - -static void write_iddata(BlendWriter *writer, ID *id) -{ - /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */ - if (id->properties && !ELEM(GS(id->name), ID_WM)) { - IDP_WriteProperty(id->properties, writer); - } - - if (id->override_library) { - BLO_write_struct(writer, IDOverrideLibrary, id->override_library); - - BLO_write_struct_list(writer, IDOverrideLibraryProperty, &id->override_library->properties); - LISTBASE_FOREACH (IDOverrideLibraryProperty *, op, &id->override_library->properties) { - BLO_write_string(writer, op->rna_path); - - BLO_write_struct_list(writer, IDOverrideLibraryPropertyOperation, &op->operations); - LISTBASE_FOREACH (IDOverrideLibraryPropertyOperation *, opop, &op->operations) { - if (opop->subitem_reference_name) { - BLO_write_string(writer, opop->subitem_reference_name); - } - if (opop->subitem_local_name) { - BLO_write_string(writer, opop->subitem_local_name); - } - } - } - } -} - static void write_previews(BlendWriter *writer, const PreviewImage *prv_orig) { /* Note we write previews also for undo steps. It takes up some memory, @@ -782,107 +683,13 @@ static void write_previews(BlendWriter *writer, const PreviewImage *prv_orig) } } -static void write_fmodifiers(BlendWriter *writer, ListBase *fmodifiers) -{ - FModifier *fcm; - - /* Write all modifiers first (for faster reloading) */ - BLO_write_struct_list(writer, FModifier, fmodifiers); - - /* Modifiers */ - for (fcm = fmodifiers->first; fcm; fcm = fcm->next) { - const FModifierTypeInfo *fmi = fmodifier_get_typeinfo(fcm); - - /* Write the specific data */ - if (fmi && fcm->data) { - /* firstly, just write the plain fmi->data struct */ - BLO_write_struct_by_name(writer, fmi->structName, fcm->data); - - /* do any modifier specific stuff */ - switch (fcm->type) { - case FMODIFIER_TYPE_GENERATOR: { - FMod_Generator *data = fcm->data; - - /* write coefficients array */ - if (data->coefficients) { - BLO_write_float_array(writer, data->arraysize, data->coefficients); - } - - break; - } - case FMODIFIER_TYPE_ENVELOPE: { - FMod_Envelope *data = fcm->data; - - /* write envelope data */ - if (data->data) { - BLO_write_struct_array(writer, FCM_EnvelopeData, data->totvert, data->data); - } - - break; - } - case FMODIFIER_TYPE_PYTHON: { - FMod_Python *data = fcm->data; - - /* Write ID Properties -- and copy this comment EXACTLY for easy finding - * of library blocks that implement this.*/ - IDP_WriteProperty(data->prop, writer); - - break; - } - } - } - } -} - -static void write_fcurves(BlendWriter *writer, ListBase *fcurves) -{ - FCurve *fcu; - - BLO_write_struct_list(writer, FCurve, fcurves); - for (fcu = fcurves->first; fcu; fcu = fcu->next) { - /* curve data */ - if (fcu->bezt) { - BLO_write_struct_array(writer, BezTriple, fcu->totvert, fcu->bezt); - } - if (fcu->fpt) { - BLO_write_struct_array(writer, FPoint, fcu->totvert, fcu->fpt); - } - - if (fcu->rna_path) { - BLO_write_string(writer, fcu->rna_path); - } - - /* driver data */ - if (fcu->driver) { - ChannelDriver *driver = fcu->driver; - DriverVar *dvar; - - BLO_write_struct(writer, ChannelDriver, driver); - - /* variables */ - BLO_write_struct_list(writer, DriverVar, &driver->variables); - for (dvar = driver->variables.first; dvar; dvar = dvar->next) { - DRIVER_TARGETS_USED_LOOPER_BEGIN (dvar) { - if (dtar->rna_path) { - BLO_write_string(writer, dtar->rna_path); - } - } - DRIVER_TARGETS_LOOPER_END; - } - } - - /* write F-Modifiers */ - write_fmodifiers(writer, &fcu->modifiers); - } -} - static void write_action(BlendWriter *writer, bAction *act, const void *id_address) { if (act->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, bAction, id_address, &act->id); - write_iddata(writer, &act->id); + BKE_id_blend_write(writer, &act->id); - write_fcurves(writer, &act->curves); + BKE_fcurve_blend_write(writer, &act->curves); LISTBASE_FOREACH (bActionGroup *, grp, &act->groups) { BLO_write_struct(writer, bActionGroup, grp); @@ -896,15 +703,12 @@ static void write_action(BlendWriter *writer, bAction *act, const void *id_addre static void write_keyingsets(BlendWriter *writer, ListBase *list) { - KeyingSet *ks; - KS_Path *ksp; - - for (ks = list->first; ks; ks = ks->next) { + LISTBASE_FOREACH (KeyingSet *, ks, list) { /* KeyingSet */ BLO_write_struct(writer, KeyingSet, ks); /* Paths */ - for (ksp = ks->paths.first; ksp; ksp = ksp->next) { + LISTBASE_FOREACH (KS_Path *, ksp, &ks->paths) { /* Path */ BLO_write_struct(writer, KS_Path, ksp); @@ -915,59 +719,6 @@ static void write_keyingsets(BlendWriter *writer, ListBase *list) } } -static void write_nlastrips(BlendWriter *writer, ListBase *strips) -{ - NlaStrip *strip; - - BLO_write_struct_list(writer, NlaStrip, strips); - for (strip = strips->first; strip; strip = strip->next) { - /* write the strip's F-Curves and modifiers */ - write_fcurves(writer, &strip->fcurves); - write_fmodifiers(writer, &strip->modifiers); - - /* write the strip's children */ - write_nlastrips(writer, &strip->strips); - } -} - -static void write_nladata(BlendWriter *writer, ListBase *nlabase) -{ - NlaTrack *nlt; - - /* write all the tracks */ - for (nlt = nlabase->first; nlt; nlt = nlt->next) { - /* write the track first */ - BLO_write_struct(writer, NlaTrack, nlt); - - /* write the track's strips */ - write_nlastrips(writer, &nlt->strips); - } -} - -static void write_animdata(BlendWriter *writer, AnimData *adt) -{ - AnimOverride *aor; - - /* firstly, just write the AnimData block */ - BLO_write_struct(writer, AnimData, adt); - - /* write drivers */ - write_fcurves(writer, &adt->drivers); - - /* write overrides */ - // FIXME: are these needed? - for (aor = adt->overrides.first; aor; aor = aor->next) { - /* overrides consist of base data + rna_path */ - BLO_write_struct(writer, AnimOverride, aor); - BLO_write_string(writer, aor->rna_path); - } - - // TODO write the remaps (if they are needed) - - /* write NLA data */ - write_nladata(writer, &adt->nla_tracks); -} - static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *sock) { if (sock->default_value == NULL) { @@ -1017,7 +768,7 @@ static void write_node_socket(BlendWriter *writer, bNodeSocket *sock) BLO_write_struct(writer, bNodeSocket, sock); if (sock->prop) { - IDP_WriteProperty(sock->prop, writer); + IDP_BlendWrite(writer, sock->prop); } write_node_socket_default_value(writer, sock); @@ -1028,7 +779,7 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock) BLO_write_struct(writer, bNodeSocket, sock); if (sock->prop) { - IDP_WriteProperty(sock->prop, writer); + IDP_BlendWrite(writer, sock->prop); } write_node_socket_default_value(writer, sock); @@ -1036,31 +787,27 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock) /* this is only direct data, tree itself should have been written */ static void write_nodetree_nolib(BlendWriter *writer, bNodeTree *ntree) { - bNode *node; - bNodeSocket *sock; - bNodeLink *link; - /* for link_list() speed, we write per list */ if (ntree->adt) { - write_animdata(writer, ntree->adt); + BKE_animdata_blend_write(writer, ntree->adt); } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BLO_write_struct(writer, bNode, node); if (node->prop) { - IDP_WriteProperty(node->prop, writer); + IDP_BlendWrite(writer, node->prop); } - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { write_node_socket(writer, sock); } - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { write_node_socket(writer, sock); } - for (link = node->internal_links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &node->internal_links) { BLO_write_struct(writer, bNodeLink, link); } @@ -1124,26 +871,26 @@ static void write_nodetree_nolib(BlendWriter *writer, bNodeTree *ntree) if (node->type == CMP_NODE_OUTPUT_FILE) { /* inputs have own storage data */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { BLO_write_struct(writer, NodeImageMultiFileSocket, sock->storage); } } if (ELEM(node->type, CMP_NODE_IMAGE, CMP_NODE_R_LAYERS)) { /* write extra socket info */ - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { BLO_write_struct(writer, NodeImageLayer, sock->storage); } } } - for (link = ntree->links.first; link; link = link->next) { + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { BLO_write_struct(writer, bNodeLink, link); } - for (sock = ntree->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { write_node_socket_interface(writer, sock); } - for (sock = ntree->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { write_node_socket_interface(writer, sock); } } @@ -1205,15 +952,15 @@ typedef struct RenderInfo { static void write_renderinfo(WriteData *wd, Main *mainvar) { bScreen *curscreen; - Scene *sce, *curscene = NULL; + Scene *curscene = NULL; ViewLayer *view_layer; - RenderInfo data; /* XXX in future, handle multiple windows with multiple screens? */ current_screen_compat(mainvar, false, &curscreen, &curscene, &view_layer); - for (sce = mainvar->scenes.first; sce; sce = sce->id.next) { + LISTBASE_FOREACH (Scene *, sce, &mainvar->scenes) { if (sce->id.lib == NULL && (sce == curscene || (sce->r.scemode & R_BG_RENDER))) { + RenderInfo data; data.sfra = sce->r.sfra; data.efra = sce->r.efra; memset(data.scene_name, 0, sizeof(data.scene_name)); @@ -1229,7 +976,7 @@ static void write_keymapitem(BlendWriter *writer, const wmKeyMapItem *kmi) { BLO_write_struct(writer, wmKeyMapItem, kmi); if (kmi->properties) { - IDP_WriteProperty(kmi->properties, writer); + IDP_BlendWrite(writer, kmi->properties); } } @@ -1262,7 +1009,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) LISTBASE_FOREACH (const wmKeyConfigPref *, kpt, &userdef->user_keyconfig_prefs) { BLO_write_struct(writer, wmKeyConfigPref, kpt); if (kpt->prop) { - IDP_WriteProperty(kpt->prop, writer); + IDP_BlendWrite(writer, kpt->prop); } } @@ -1273,7 +1020,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) const bUserMenuItem_Op *umi_op = (const bUserMenuItem_Op *)umi; BLO_write_struct(writer, bUserMenuItem_Op, umi_op); if (umi_op->prop) { - IDP_WriteProperty(umi_op->prop, writer); + IDP_BlendWrite(writer, umi_op->prop); } } else if (umi->type == USER_MENU_TYPE_MENU) { @@ -1293,7 +1040,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) LISTBASE_FOREACH (const bAddon *, bext, &userdef->addons) { BLO_write_struct(writer, bAddon, bext); if (bext->prop) { - IDP_WriteProperty(bext->prop, writer); + IDP_BlendWrite(writer, bext->prop); } } @@ -1308,11 +1055,9 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) static void write_boid_state(BlendWriter *writer, BoidState *state) { - BoidRule *rule = state->rules.first; - BLO_write_struct(writer, BoidState, state); - for (; rule; rule = rule->next) { + LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { switch (rule->type) { case eBoidRuleType_Goal: case eBoidRuleType_Avoid: @@ -1361,21 +1106,14 @@ static const char *ptcache_extra_struct[] = { }; static void write_pointcaches(BlendWriter *writer, ListBase *ptcaches) { - PointCache *cache = ptcaches->first; - int i; - - for (; cache; cache = cache->next) { + LISTBASE_FOREACH (PointCache *, cache, ptcaches) { BLO_write_struct(writer, PointCache, cache); if ((cache->flag & PTCACHE_DISK_CACHE) == 0) { - PTCacheMem *pm = cache->mem_cache.first; - - for (; pm; pm = pm->next) { - PTCacheExtra *extra = pm->extradata.first; - + LISTBASE_FOREACH (PTCacheMem *, pm, &cache->mem_cache) { BLO_write_struct(writer, PTCacheMem, pm); - for (i = 0; i < BPHYS_TOT_DATA; i++) { + for (int i = 0; i < BPHYS_TOT_DATA; i++) { if (pm->data[i] && pm->data_types & (1 << i)) { if (ptcache_data_struct[i][0] == '\0') { BLO_write_raw(writer, MEM_allocN_len(pm->data[i]), pm->data[i]); @@ -1387,7 +1125,7 @@ static void write_pointcaches(BlendWriter *writer, ListBase *ptcaches) } } - for (; extra; extra = extra->next) { + LISTBASE_FOREACH (PTCacheExtra *, extra, &pm->extradata) { if (ptcache_extra_struct[extra->type][0] == '\0') { continue; } @@ -1407,10 +1145,10 @@ static void write_particlesettings(BlendWriter *writer, if (part->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, ParticleSettings, id_address, &part->id); - write_iddata(writer, &part->id); + BKE_id_blend_write(writer, &part->id); if (part->adt) { - write_animdata(writer, part->adt); + BKE_animdata_blend_write(writer, part->adt); } BLO_write_struct(writer, PartDeflect, part->pd); BLO_write_struct(writer, PartDeflect, part->pd2); @@ -1464,11 +1202,7 @@ static void write_particlesettings(BlendWriter *writer, static void write_particlesystems(BlendWriter *writer, ListBase *particles) { - ParticleSystem *psys = particles->first; - ParticleTarget *pt; - int a; - - for (; psys; psys = psys->next) { + LISTBASE_FOREACH (ParticleSystem *, psys, particles) { BLO_write_struct(writer, ParticleSystem, psys); if (psys->particles) { @@ -1477,7 +1211,7 @@ static void write_particlesystems(BlendWriter *writer, ListBase *particles) if (psys->particles->hair) { ParticleData *pa = psys->particles; - for (a = 0; a < psys->totpart; a++, pa++) { + for (int a = 0; a < psys->totpart; a++, pa++) { BLO_write_struct_array(writer, HairKey, pa->totkey, pa->hair); } } @@ -1492,8 +1226,7 @@ static void write_particlesystems(BlendWriter *writer, ListBase *particles) writer, ParticleSpring, psys->tot_fluidsprings, psys->fluid_springs); } } - pt = psys->targets.first; - for (; pt; pt = pt->next) { + LISTBASE_FOREACH (ParticleTarget *, pt, &psys->targets) { BLO_write_struct(writer, ParticleTarget, pt); } @@ -1527,9 +1260,7 @@ static void write_motionpath(BlendWriter *writer, bMotionPath *mpath) static void write_constraints(BlendWriter *writer, ListBase *conlist) { - bConstraint *con; - - for (con = conlist->first; con; con = con->next) { + LISTBASE_FOREACH (bConstraint *, con, conlist) { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); /* Write the specific data */ @@ -1541,25 +1272,23 @@ static void write_constraints(BlendWriter *writer, ListBase *conlist) switch (con->type) { case CONSTRAINT_TYPE_PYTHON: { bPythonConstraint *data = con->data; - bConstraintTarget *ct; /* write targets */ - for (ct = data->targets.first; ct; ct = ct->next) { + LISTBASE_FOREACH (bConstraintTarget *, ct, &data->targets) { BLO_write_struct(writer, bConstraintTarget, ct); } /* Write ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ - IDP_WriteProperty(data->prop, writer); + IDP_BlendWrite(writer, data->prop); break; } case CONSTRAINT_TYPE_ARMATURE: { bArmatureConstraint *data = con->data; - bConstraintTarget *ct; /* write targets */ - for (ct = data->targets.first; ct; ct = ct->next) { + LISTBASE_FOREACH (bConstraintTarget *, ct, &data->targets) { BLO_write_struct(writer, bConstraintTarget, ct); } @@ -1583,9 +1312,6 @@ static void write_constraints(BlendWriter *writer, ListBase *conlist) static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm) { - bPoseChannel *chan; - bActionGroup *grp; - /* Write each channel */ if (pose == NULL) { return; @@ -1594,11 +1320,11 @@ static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm) BLI_assert(arm != NULL); /* Write channels */ - for (chan = pose->chanbase.first; chan; chan = chan->next) { + LISTBASE_FOREACH (bPoseChannel *, chan, &pose->chanbase) { /* Write ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ if (chan->prop) { - IDP_WriteProperty(chan->prop, writer); + IDP_BlendWrite(writer, chan->prop); } write_constraints(writer, &chan->constraints); @@ -1620,7 +1346,7 @@ static void write_pose(BlendWriter *writer, bPose *pose, bArmature *arm) } /* Write groups */ - for (grp = pose->agroups.first; grp; grp = grp->next) { + LISTBASE_FOREACH (bActionGroup *, grp, &pose->agroups) { BLO_write_struct(writer, bActionGroup, grp); } @@ -1652,13 +1378,11 @@ static void write_fmaps(BlendWriter *writer, ListBase *fbase) static void write_modifiers(BlendWriter *writer, ListBase *modbase) { - ModifierData *md; - if (modbase == NULL) { return; } - for (md = modbase->first; md; md = md->next) { + LISTBASE_FOREACH (ModifierData *, md, modbase) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (mti == NULL) { return; @@ -1717,15 +1441,14 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase) DynamicPaintModifierData *pmd = (DynamicPaintModifierData *)md; if (pmd->canvas) { - DynamicPaintSurface *surface; BLO_write_struct(writer, DynamicPaintCanvasSettings, pmd->canvas); /* write surfaces */ - for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { BLO_write_struct(writer, DynamicPaintSurface, surface); } /* write caches and effector weights */ - for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { + LISTBASE_FOREACH (DynamicPaintSurface *, surface, &pmd->canvas->surfaces) { write_pointcaches(writer, &(surface->ptcaches)); BLO_write_struct(writer, EffectorWeights, surface->effector_weights); @@ -1757,13 +1480,11 @@ static void write_modifiers(BlendWriter *writer, ListBase *modbase) static void write_gpencil_modifiers(BlendWriter *writer, ListBase *modbase) { - GpencilModifierData *md; - if (modbase == NULL) { return; } - for (md = modbase->first; md; md = md->next) { + LISTBASE_FOREACH (GpencilModifierData *, md, modbase) { const GpencilModifierTypeInfo *mti = BKE_gpencil_modifier_get_info(md->type); if (mti == NULL) { return; @@ -1824,13 +1545,11 @@ static void write_gpencil_modifiers(BlendWriter *writer, ListBase *modbase) static void write_shaderfxs(BlendWriter *writer, ListBase *fxbase) { - ShaderFxData *fx; - if (fxbase == NULL) { return; } - for (fx = fxbase->first; fx; fx = fx->next) { + LISTBASE_FOREACH (ShaderFxData *, fx, fxbase) { const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type); if (fxi == NULL) { return; @@ -1848,10 +1567,10 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address /* write LibData */ BLO_write_id_struct(writer, Object, id_address, &ob->id); - write_iddata(writer, &ob->id); + BKE_id_blend_write(writer, &ob->id); if (ob->adt) { - write_animdata(writer, ob->adt); + BKE_animdata_blend_write(writer, ob->adt); } /* direct data */ @@ -1875,9 +1594,9 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address BLO_write_struct(writer, PartDeflect, ob->pd); if (ob->soft) { - /* Don't write ADMM-PD data. */ - ADMMPDInterfaceData *admmpd = ob->soft->admmpd; - ob->soft->admmpd = NULL; + /* Don't write ADMM-PD data. Just recompute when needed. */ + ListBase *admmpd_list = ob->soft->shared->admmpd_list; + ob->soft->shared->admmpd_list = NULL; /* Set deprecated pointers to prevent crashes of older Blenders */ ob->soft->pointcache = ob->soft->shared->pointcache; ob->soft->ptcaches = ob->soft->shared->ptcaches; @@ -1885,8 +1604,8 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address BLO_write_struct(writer, SoftBody_Shared, ob->soft->shared); write_pointcaches(writer, &(ob->soft->shared->ptcaches)); BLO_write_struct(writer, EffectorWeights, ob->soft->effector_weights); - /* Reset the ADMM-PD data pointer */ - ob->soft->admmpd = admmpd; + /* Reset the ADMM-PD data pointer. */ + ob->soft->shared->admmpd_list = admmpd_list; } if (ob->rigidbody_object) { @@ -1907,7 +1626,6 @@ static void write_object(BlendWriter *writer, Object *ob, const void *id_address write_shaderfxs(writer, &ob->shader_fx); BLO_write_struct_list(writer, LinkData, &ob->pc_ids); - BLO_write_struct_list(writer, LodLevel, &ob->lodlevels); write_previews(writer, ob->preview); } @@ -1922,7 +1640,7 @@ static void write_vfont(BlendWriter *writer, VFont *vf, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, VFont, id_address, &vf->id); - write_iddata(writer, &vf->id); + BKE_id_blend_write(writer, &vf->id); /* direct data */ if (vf->packedfile) { @@ -1938,10 +1656,10 @@ static void write_key(BlendWriter *writer, Key *key, const void *id_address) if (key->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Key, id_address, &key->id); - write_iddata(writer, &key->id); + BKE_id_blend_write(writer, &key->id); if (key->adt) { - write_animdata(writer, key->adt); + BKE_animdata_blend_write(writer, key->adt); } /* direct data */ @@ -1959,10 +1677,10 @@ static void write_camera(BlendWriter *writer, Camera *cam, const void *id_addres if (cam->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Camera, id_address, &cam->id); - write_iddata(writer, &cam->id); + BKE_id_blend_write(writer, &cam->id); if (cam->adt) { - write_animdata(writer, cam->adt); + BKE_animdata_blend_write(writer, cam->adt); } LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) { @@ -1984,12 +1702,12 @@ static void write_mball(BlendWriter *writer, MetaBall *mb, const void *id_addres /* write LibData */ BLO_write_id_struct(writer, MetaBall, id_address, &mb->id); - write_iddata(writer, &mb->id); + BKE_id_blend_write(writer, &mb->id); /* direct data */ BLO_write_pointer_array(writer, mb->totcol, mb->mat); if (mb->adt) { - write_animdata(writer, mb->adt); + BKE_animdata_blend_write(writer, mb->adt); } LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { @@ -2008,12 +1726,12 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, Curve, id_address, &cu->id); - write_iddata(writer, &cu->id); + BKE_id_blend_write(writer, &cu->id); /* direct data */ BLO_write_pointer_array(writer, cu->totcol, cu->mat); if (cu->adt) { - write_animdata(writer, cu->adt); + BKE_animdata_blend_write(writer, cu->adt); } if (cu->vfont) { @@ -2045,206 +1763,6 @@ static void write_curve(BlendWriter *writer, Curve *cu, const void *id_address) } } -static void write_dverts(BlendWriter *writer, int count, MDeformVert *dvlist) -{ - if (dvlist) { - - /* Write the dvert list */ - BLO_write_struct_array(writer, MDeformVert, count, dvlist); - - /* Write deformation data for each dvert */ - for (int i = 0; i < count; i++) { - if (dvlist[i].dw) { - BLO_write_struct_array(writer, MDeformWeight, dvlist[i].totweight, dvlist[i].dw); - } - } - } -} - -static void write_mdisps(BlendWriter *writer, int count, MDisps *mdlist, int external) -{ - if (mdlist) { - int i; - - BLO_write_struct_array(writer, MDisps, count, mdlist); - for (i = 0; i < count; i++) { - MDisps *md = &mdlist[i]; - if (md->disps) { - if (!external) { - BLO_write_float3_array(writer, md->totdisp, &md->disps[0][0]); - } - } - - if (md->hidden) { - BLO_write_raw(writer, BLI_BITMAP_SIZE(md->totdisp), md->hidden); - } - } - } -} - -static void write_grid_paint_mask(BlendWriter *writer, int count, GridPaintMask *grid_paint_mask) -{ - if (grid_paint_mask) { - int i; - - BLO_write_struct_array(writer, GridPaintMask, count, grid_paint_mask); - for (i = 0; i < count; i++) { - GridPaintMask *gpm = &grid_paint_mask[i]; - if (gpm->data) { - const int gridsize = BKE_ccg_gridsize(gpm->level); - BLO_write_raw(writer, sizeof(*gpm->data) * gridsize * gridsize, gpm->data); - } - } - } -} - -static void write_customdata(BlendWriter *writer, - ID *id, - int count, - CustomData *data, - CustomDataLayer *layers, - CustomDataMask cddata_mask) -{ - int i; - - /* write external customdata (not for undo) */ - if (data->external && !BLO_write_is_undo(writer)) { - CustomData_external_write(data, id, cddata_mask, count, 0); - } - - BLO_write_struct_array_at_address(writer, CustomDataLayer, data->totlayer, data->layers, layers); - - for (i = 0; i < data->totlayer; i++) { - CustomDataLayer *layer = &layers[i]; - const char *structname; - int structnum, datasize; - - if (layer->type == CD_MDEFORMVERT) { - /* layer types that allocate own memory need special handling */ - write_dverts(writer, count, layer->data); - } - else if (layer->type == CD_MDISPS) { - write_mdisps(writer, count, layer->data, layer->flag & CD_FLAG_EXTERNAL); - } - else if (layer->type == CD_PAINT_MASK) { - const float *layer_data = layer->data; - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else if (layer->type == CD_SCULPT_FACE_SETS) { - const float *layer_data = layer->data; - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else if (layer->type == CD_GRID_PAINT_MASK) { - write_grid_paint_mask(writer, count, layer->data); - } - else if (layer->type == CD_FACEMAP) { - const int *layer_data = layer->data; - BLO_write_raw(writer, sizeof(*layer_data) * count, layer_data); - } - else { - CustomData_file_write_info(layer->type, &structname, &structnum); - if (structnum) { - datasize = structnum * count; - BLO_write_struct_array_by_name(writer, structname, datasize, layer->data); - } - else if (!BLO_write_is_undo(writer)) { /* Do not warn on undo. */ - printf("%s error: layer '%s':%d - can't be written to file\n", - __func__, - structname, - layer->type); - } - } - } - - if (data->external) { - BLO_write_struct(writer, CustomDataExternal, data->external); - } -} - -static void write_mesh(BlendWriter *writer, Mesh *mesh, const void *id_address) -{ - if (mesh->id.us > 0 || BLO_write_is_undo(writer)) { - /* cache only - don't write */ - mesh->mface = NULL; - mesh->totface = 0; - memset(&mesh->fdata, 0, sizeof(mesh->fdata)); - memset(&mesh->runtime, 0, sizeof(mesh->runtime)); - - /* Reduce xdata layers, fill xlayers with layers to be written. - * This makes xdata invalid for Blender, which is why we made a - * temporary local copy. */ - CustomDataLayer *vlayers = NULL, vlayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *elayers = NULL, elayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *flayers = NULL, flayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *llayers = NULL, llayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - - CustomData_file_write_prepare(&mesh->vdata, &vlayers, vlayers_buff, ARRAY_SIZE(vlayers_buff)); - CustomData_file_write_prepare(&mesh->edata, &elayers, elayers_buff, ARRAY_SIZE(elayers_buff)); - flayers = flayers_buff; - CustomData_file_write_prepare(&mesh->ldata, &llayers, llayers_buff, ARRAY_SIZE(llayers_buff)); - CustomData_file_write_prepare(&mesh->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - - BLO_write_id_struct(writer, Mesh, id_address, &mesh->id); - write_iddata(writer, &mesh->id); - - /* direct data */ - if (mesh->adt) { - write_animdata(writer, mesh->adt); - } - - BLO_write_pointer_array(writer, mesh->totcol, mesh->mat); - BLO_write_raw(writer, sizeof(MSelect) * mesh->totselect, mesh->mselect); - - write_customdata(writer, &mesh->id, mesh->totvert, &mesh->vdata, vlayers, CD_MASK_MESH.vmask); - write_customdata(writer, &mesh->id, mesh->totedge, &mesh->edata, elayers, CD_MASK_MESH.emask); - /* fdata is really a dummy - written so slots align */ - write_customdata(writer, &mesh->id, mesh->totface, &mesh->fdata, flayers, CD_MASK_MESH.fmask); - write_customdata(writer, &mesh->id, mesh->totloop, &mesh->ldata, llayers, CD_MASK_MESH.lmask); - write_customdata(writer, &mesh->id, mesh->totpoly, &mesh->pdata, players, CD_MASK_MESH.pmask); - - /* free temporary data */ - if (vlayers && vlayers != vlayers_buff) { - MEM_freeN(vlayers); - } - if (elayers && elayers != elayers_buff) { - MEM_freeN(elayers); - } - if (flayers && flayers != flayers_buff) { - MEM_freeN(flayers); - } - if (llayers && llayers != llayers_buff) { - MEM_freeN(llayers); - } - if (players && players != players_buff) { - MEM_freeN(players); - } - } -} - -static void write_lattice(BlendWriter *writer, Lattice *lt, const void *id_address) -{ - if (lt->id.us > 0 || BLO_write_is_undo(writer)) { - /* Clean up, important in undo case to reduce false detection of changed datablocks. */ - lt->editlatt = NULL; - lt->batch_cache = NULL; - - /* write LibData */ - BLO_write_id_struct(writer, Lattice, id_address, <->id); - write_iddata(writer, <->id); - - /* write animdata */ - if (lt->adt) { - write_animdata(writer, lt->adt); - } - - /* direct data */ - BLO_write_struct_array(writer, BPoint, lt->pntsu * lt->pntsv * lt->pntsw, lt->def); - - write_dverts(writer, lt->pntsu * lt->pntsv * lt->pntsw, lt->dvert); - } -} - static void write_image(BlendWriter *writer, Image *ima, const void *id_address) { if (ima->id.us > 0 || BLO_write_is_undo(writer)) { @@ -2259,7 +1777,7 @@ static void write_image(BlendWriter *writer, Image *ima, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, Image, id_address, &ima->id); - write_iddata(writer, &ima->id); + BKE_id_blend_write(writer, &ima->id); for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { BLO_write_struct(writer, ImagePackedFile, imapf); @@ -2290,10 +1808,10 @@ static void write_texture(BlendWriter *writer, Tex *tex, const void *id_address) if (tex->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Tex, id_address, &tex->id); - write_iddata(writer, &tex->id); + BKE_id_blend_write(writer, &tex->id); if (tex->adt) { - write_animdata(writer, tex->adt); + BKE_animdata_blend_write(writer, tex->adt); } /* direct data */ @@ -2320,10 +1838,10 @@ static void write_material(BlendWriter *writer, Material *ma, const void *id_add /* write LibData */ BLO_write_id_struct(writer, Material, id_address, &ma->id); - write_iddata(writer, &ma->id); + BKE_id_blend_write(writer, &ma->id); if (ma->adt) { - write_animdata(writer, ma->adt); + BKE_animdata_blend_write(writer, ma->adt); } /* nodetree is integral part of material, no libdata */ @@ -2349,10 +1867,10 @@ static void write_world(BlendWriter *writer, World *wrld, const void *id_address /* write LibData */ BLO_write_id_struct(writer, World, id_address, &wrld->id); - write_iddata(writer, &wrld->id); + BKE_id_blend_write(writer, &wrld->id); if (wrld->adt) { - write_animdata(writer, wrld->adt); + BKE_animdata_blend_write(writer, wrld->adt); } /* nodetree is integral part of world, no libdata */ @@ -2370,10 +1888,10 @@ static void write_light(BlendWriter *writer, Light *la, const void *id_address) if (la->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Light, id_address, &la->id); - write_iddata(writer, &la->id); + BKE_id_blend_write(writer, &la->id); if (la->adt) { - write_animdata(writer, la->adt); + BKE_animdata_blend_write(writer, la->adt); } if (la->curfalloff) { @@ -2415,7 +1933,7 @@ static void write_collection(BlendWriter *writer, Collection *collection, const /* write LibData */ BLO_write_id_struct(writer, Collection, id_address, &collection->id); - write_iddata(writer, &collection->id); + BKE_id_blend_write(writer, &collection->id); write_collection_nolib(writer, collection); } @@ -2423,9 +1941,7 @@ static void write_collection(BlendWriter *writer, Collection *collection, const static void write_sequence_modifiers(BlendWriter *writer, ListBase *modbase) { - SequenceModifierData *smd; - - for (smd = modbase->first; smd; smd = smd->next) { + LISTBASE_FOREACH (SequenceModifierData *, smd, modbase) { const SequenceModifierTypeInfo *smti = BKE_sequence_modifier_type_info_get(smd->type); if (smti) { @@ -2458,7 +1974,7 @@ static void write_view_settings(BlendWriter *writer, ColorManagedViewSettings *v static void write_view3dshading(BlendWriter *writer, View3DShading *shading) { if (shading->prop) { - IDP_WriteProperty(shading->prop, writer); + IDP_BlendWrite(writer, shading->prop); } } @@ -2485,7 +2001,7 @@ static void write_view_layer(BlendWriter *writer, ViewLayer *view_layer) BLO_write_struct_list(writer, Base, &view_layer->object_bases); if (view_layer->id_properties) { - IDP_WriteProperty(view_layer->id_properties, writer); + IDP_BlendWrite(writer, view_layer->id_properties); } LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { @@ -2543,10 +2059,10 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, Scene, id_address, &sce->id); - write_iddata(writer, &sce->id); + BKE_id_blend_write(writer, &sce->id); if (sce->adt) { - write_animdata(writer, sce->adt); + BKE_animdata_blend_write(writer, sce->adt); } write_keyingsets(writer, &sce->keyingsets); @@ -2612,15 +2128,15 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address) /* reset write flags too */ - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { if (seq->strip) { seq->strip->done = false; } BLO_write_struct(writer, Sequence, seq); } - SEQ_END; + SEQ_ALL_END; - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { if (seq->strip && seq->strip->done == 0) { /* write strip with 'done' at 0 because readfile */ @@ -2680,12 +2196,12 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address) } if (seq->prop) { - IDP_WriteProperty(seq->prop, writer); + IDP_BlendWrite(writer, seq->prop); } write_sequence_modifiers(writer, &seq->modifiers); } - SEQ_END; + SEQ_ALL_END; /* new; meta stack too, even when its nasty restore code */ LISTBASE_FOREACH (MetaStack *, ms, &ed->metastack) { @@ -2703,7 +2219,7 @@ static void write_scene(BlendWriter *writer, Scene *sce, const void *id_address) } } if (sce->r.ffcodecdata.properties) { - IDP_WriteProperty(sce->r.ffcodecdata.properties, writer); + IDP_BlendWrite(writer, sce->r.ffcodecdata.properties); } /* writing dynamic list of TimeMarkers to the blend file */ @@ -2777,10 +2293,10 @@ static void write_gpencil(BlendWriter *writer, bGPdata *gpd, const void *id_addr /* write gpd data block to file */ BLO_write_id_struct(writer, bGPdata, id_address, &gpd->id); - write_iddata(writer, &gpd->id); + BKE_id_blend_write(writer, &gpd->id); if (gpd->adt) { - write_animdata(writer, gpd->adt); + BKE_animdata_blend_write(writer, gpd->adt); } BLO_write_pointer_array(writer, gpd->totcol, gpd->mat); @@ -2798,7 +2314,7 @@ static void write_gpencil(BlendWriter *writer, bGPdata *gpd, const void *id_addr LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { BLO_write_struct_array(writer, bGPDspoint, gps->totpoints, gps->points); BLO_write_struct_array(writer, bGPDtriangle, gps->tot_triangles, gps->triangles); - write_dverts(writer, gps->totpoints, gps->dvert); + BKE_defvert_blend_write(writer, gps->totpoints, gps->dvert); } } } @@ -2847,7 +2363,7 @@ static void write_uilist(BlendWriter *writer, uiList *ui_list) BLO_write_struct(writer, uiList, ui_list); if (ui_list->properties) { - IDP_WriteProperty(ui_list->properties, writer); + IDP_BlendWrite(writer, ui_list->properties); } } @@ -2993,18 +2509,16 @@ static void write_area_regions(BlendWriter *writer, ScrArea *area) } else if (sl->spacetype == SPACE_NODE) { SpaceNode *snode = (SpaceNode *)sl; - bNodeTreePath *path; BLO_write_struct(writer, SpaceNode, snode); - for (path = snode->treepath.first; path; path = path->next) { + LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) { BLO_write_struct(writer, bNodeTreePath, path); } } else if (sl->spacetype == SPACE_CONSOLE) { SpaceConsole *con = (SpaceConsole *)sl; - ConsoleLine *cl; - for (cl = con->history.first; cl; cl = cl->next) { + LISTBASE_FOREACH (ConsoleLine *, cl, &con->history) { /* 'len_alloc' is invalid on write, set from 'len' on read */ BLO_write_struct(writer, ConsoleLine, cl); BLO_write_raw(writer, cl->len + 1, cl->line); @@ -3053,7 +2567,7 @@ static void write_area_map(BlendWriter *writer, ScrAreaMap *area_map) static void write_windowmanager(BlendWriter *writer, wmWindowManager *wm, const void *id_address) { BLO_write_id_struct(writer, wmWindowManager, id_address, &wm->id); - write_iddata(writer, &wm->id); + BKE_id_blend_write(writer, &wm->id); write_wm_xr_data(writer, &wm->xr); LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { @@ -3088,7 +2602,7 @@ static void write_screen(BlendWriter *writer, bScreen *screen, const void *id_ad /* write LibData */ /* in 2.50+ files, the file identifier for screens is patched, forward compatibility */ writestruct_at_address(writer->wd, ID_SCRN, bScreen, 1, id_address, screen); - write_iddata(writer, &screen->id); + BKE_id_blend_write(writer, &screen->id); write_previews(writer, screen->preview); @@ -3108,7 +2622,7 @@ static void write_bone(BlendWriter *writer, Bone *bone) /* Write ID Properties -- and copy this comment EXACTLY for easy finding * of library blocks that implement this.*/ if (bone->prop) { - IDP_WriteProperty(bone->prop, writer); + IDP_BlendWrite(writer, bone->prop); } /* Write Children */ @@ -3128,10 +2642,10 @@ static void write_armature(BlendWriter *writer, bArmature *arm, const void *id_a arm->act_edbone = NULL; BLO_write_id_struct(writer, bArmature, id_address, &arm->id); - write_iddata(writer, &arm->id); + BKE_id_blend_write(writer, &arm->id); if (arm->adt) { - write_animdata(writer, arm->adt); + BKE_animdata_blend_write(writer, arm->adt); } /* Direct data */ @@ -3153,7 +2667,7 @@ static void write_text(BlendWriter *writer, Text *text, const void *id_address) /* write LibData */ BLO_write_id_struct(writer, Text, id_address, &text->id); - write_iddata(writer, &text->id); + BKE_id_blend_write(writer, &text->id); if (text->filepath) { BLO_write_string(writer, text->filepath); @@ -3176,10 +2690,10 @@ static void write_speaker(BlendWriter *writer, Speaker *spk, const void *id_addr if (spk->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, Speaker, id_address, &spk->id); - write_iddata(writer, &spk->id); + BKE_id_blend_write(writer, &spk->id); if (spk->adt) { - write_animdata(writer, spk->adt); + BKE_animdata_blend_write(writer, spk->adt); } } } @@ -3195,7 +2709,7 @@ static void write_sound(BlendWriter *writer, bSound *sound, const void *id_addre /* write LibData */ BLO_write_id_struct(writer, bSound, id_address, &sound->id); - write_iddata(writer, &sound->id); + BKE_id_blend_write(writer, &sound->id); if (sound->packedfile) { PackedFile *pf = sound->packedfile; @@ -3210,10 +2724,10 @@ static void write_probe(BlendWriter *writer, LightProbe *prb, const void *id_add if (prb->id.us > 0 || BLO_write_is_undo(writer)) { /* write LibData */ BLO_write_id_struct(writer, LightProbe, id_address, &prb->id); - write_iddata(writer, &prb->id); + BKE_id_blend_write(writer, &prb->id); if (prb->adt) { - write_animdata(writer, prb->adt); + BKE_animdata_blend_write(writer, prb->adt); } } } @@ -3232,7 +2746,7 @@ static void write_nodetree(BlendWriter *writer, bNodeTree *ntree, const void *id BLO_write_id_struct(writer, bNodeTree, id_address, &ntree->id); /* Note that trees directly used by other IDs (materials etc.) are not 'real' ID, they cannot * be linked, etc., so we write actual id data here only, for 'real' ID trees. */ - write_iddata(writer, &ntree->id); + BKE_id_blend_write(writer, &ntree->id); write_nodetree_nolib(writer, ntree); } @@ -3242,7 +2756,7 @@ static void write_brush(BlendWriter *writer, Brush *brush, const void *id_addres { if (brush->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, Brush, id_address, &brush->id); - write_iddata(writer, &brush->id); + BKE_id_blend_write(writer, &brush->id); if (brush->curve) { BKE_curvemapping_blend_write(writer, brush->curve); @@ -3290,7 +2804,7 @@ static void write_palette(BlendWriter *writer, Palette *palette, const void *id_ if (palette->id.us > 0 || BLO_write_is_undo(writer)) { PaletteColor *color; BLO_write_id_struct(writer, Palette, id_address, &palette->id); - write_iddata(writer, &palette->id); + BKE_id_blend_write(writer, &palette->id); for (color = palette->colors.first; color; color = color->next) { BLO_write_struct(writer, PaletteColor, color); @@ -3302,7 +2816,7 @@ static void write_paintcurve(BlendWriter *writer, PaintCurve *pc, const void *id { if (pc->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id); - write_iddata(writer, &pc->id); + BKE_id_blend_write(writer, &pc->id); BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points); } @@ -3358,10 +2872,10 @@ static void write_movieclip(BlendWriter *writer, MovieClip *clip, const void *id MovieTrackingObject *object; BLO_write_id_struct(writer, MovieClip, id_address, &clip->id); - write_iddata(writer, &clip->id); + BKE_id_blend_write(writer, &clip->id); if (clip->adt) { - write_animdata(writer, clip->adt); + BKE_animdata_blend_write(writer, clip->adt); } write_movieTracks(writer, &tracking->tracks); @@ -3387,10 +2901,10 @@ static void write_mask(BlendWriter *writer, Mask *mask, const void *id_address) MaskLayer *masklay; BLO_write_id_struct(writer, Mask, id_address, &mask->id); - write_iddata(writer, &mask->id); + BKE_id_blend_write(writer, &mask->id); if (mask->adt) { - write_animdata(writer, mask->adt); + BKE_animdata_blend_write(writer, mask->adt); } for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { @@ -3697,10 +3211,10 @@ static void write_linestyle(BlendWriter *writer, { if (linestyle->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, FreestyleLineStyle, id_address, &linestyle->id); - write_iddata(writer, &linestyle->id); + BKE_id_blend_write(writer, &linestyle->id); if (linestyle->adt) { - write_animdata(writer, linestyle->adt); + BKE_animdata_blend_write(writer, linestyle->adt); } write_linestyle_color_modifiers(writer, &linestyle->color_modifiers); @@ -3731,7 +3245,7 @@ static void write_cachefile(BlendWriter *writer, CacheFile *cache_file, const vo BLO_write_id_struct(writer, CacheFile, id_address, &cache_file->id); if (cache_file->adt) { - write_animdata(writer, cache_file->adt); + BKE_animdata_blend_write(writer, cache_file->adt); } } } @@ -3739,14 +3253,14 @@ static void write_cachefile(BlendWriter *writer, CacheFile *cache_file, const vo static void write_workspace(BlendWriter *writer, WorkSpace *workspace, const void *id_address) { BLO_write_id_struct(writer, WorkSpace, id_address, &workspace->id); - write_iddata(writer, &workspace->id); + BKE_id_blend_write(writer, &workspace->id); BLO_write_struct_list(writer, WorkSpaceLayout, &workspace->layouts); BLO_write_struct_list(writer, WorkSpaceDataRelation, &workspace->hook_layout_relations); BLO_write_struct_list(writer, wmOwnerID, &workspace->owner_ids); BLO_write_struct_list(writer, bToolRef, &workspace->tools); LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { if (tref->properties) { - IDP_WriteProperty(tref->properties, writer); + IDP_BlendWrite(writer, tref->properties); } } } @@ -3754,29 +3268,16 @@ static void write_workspace(BlendWriter *writer, WorkSpace *workspace, const voi static void write_hair(BlendWriter *writer, Hair *hair, const void *id_address) { if (hair->id.us > 0 || BLO_write_is_undo(writer)) { - CustomDataLayer *players = NULL, players_buff[CD_TEMP_CHUNK_SIZE]; - CustomDataLayer *clayers = NULL, clayers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_file_write_prepare(&hair->pdata, &players, players_buff, ARRAY_SIZE(players_buff)); - CustomData_file_write_prepare(&hair->cdata, &clayers, clayers_buff, ARRAY_SIZE(clayers_buff)); - /* Write LibData */ BLO_write_id_struct(writer, Hair, id_address, &hair->id); - write_iddata(writer, &hair->id); + BKE_id_blend_write(writer, &hair->id); /* Direct data */ - write_customdata(writer, &hair->id, hair->totpoint, &hair->pdata, players, CD_MASK_ALL); - write_customdata(writer, &hair->id, hair->totcurve, &hair->cdata, clayers, CD_MASK_ALL); + CustomData_blend_write(writer, &hair->pdata, hair->totpoint, CD_MASK_ALL, &hair->id); + CustomData_blend_write(writer, &hair->cdata, hair->totcurve, CD_MASK_ALL, &hair->id); BLO_write_pointer_array(writer, hair->totcol, hair->mat); if (hair->adt) { - write_animdata(writer, hair->adt); - } - - /* Remove temporary data. */ - if (players && players != players_buff) { - MEM_freeN(players); - } - if (clayers && clayers != clayers_buff) { - MEM_freeN(clayers); + BKE_animdata_blend_write(writer, hair->adt); } } } @@ -3790,14 +3291,14 @@ static void write_pointcloud(BlendWriter *writer, PointCloud *pointcloud, const /* Write LibData */ BLO_write_id_struct(writer, PointCloud, id_address, &pointcloud->id); - write_iddata(writer, &pointcloud->id); + BKE_id_blend_write(writer, &pointcloud->id); /* Direct data */ - write_customdata( - writer, &pointcloud->id, pointcloud->totpoint, &pointcloud->pdata, players, CD_MASK_ALL); + CustomData_blend_write( + writer, &pointcloud->pdata, pointcloud->totpoint, CD_MASK_ALL, &pointcloud->id); BLO_write_pointer_array(writer, pointcloud->totcol, pointcloud->mat); if (pointcloud->adt) { - write_animdata(writer, pointcloud->adt); + BKE_animdata_blend_write(writer, pointcloud->adt); } /* Remove temporary data. */ @@ -3815,12 +3316,12 @@ static void write_volume(BlendWriter *writer, Volume *volume, const void *id_add /* write LibData */ BLO_write_id_struct(writer, Volume, id_address, &volume->id); - write_iddata(writer, &volume->id); + BKE_id_blend_write(writer, &volume->id); /* direct data */ BLO_write_pointer_array(writer, volume->totcol, volume->mat); if (volume->adt) { - write_animdata(writer, volume->adt); + BKE_animdata_blend_write(writer, volume->adt); } if (volume->packedfile) { @@ -3835,10 +3336,10 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const { if (simulation->id.us > 0 || BLO_write_is_undo(writer)) { BLO_write_id_struct(writer, Simulation, id_address, &simulation->id); - write_iddata(writer, &simulation->id); + BKE_id_blend_write(writer, &simulation->id); if (simulation->adt) { - write_animdata(writer, simulation->adt); + BKE_animdata_blend_write(writer, simulation->adt); } /* nodetree is integral part of simulation, no libdata */ @@ -3855,21 +3356,11 @@ static void write_simulation(BlendWriter *writer, Simulation *simulation, const ParticleSimulationState *particle_state = (ParticleSimulationState *)state; BLO_write_struct(writer, ParticleSimulationState, particle_state); - CustomDataLayer *layers = NULL; - CustomDataLayer layers_buff[CD_TEMP_CHUNK_SIZE]; - CustomData_file_write_prepare( - &particle_state->attributes, &layers, layers_buff, ARRAY_SIZE(layers_buff)); - - write_customdata(writer, - &simulation->id, - particle_state->tot_particles, - &particle_state->attributes, - layers, - CD_MASK_ALL); - - if (layers != NULL && layers != layers_buff) { - MEM_freeN(layers); - } + CustomData_blend_write(writer, + &particle_state->attributes, + particle_state->tot_particles, + CD_MASK_ALL, + &simulation->id); } else if (STREQ(state->type, SIM_TYPE_NAME_PARTICLE_MESH_EMITTER)) { ParticleMeshEmitterSimulationState *emitter_state = (ParticleMeshEmitterSimulationState *) @@ -3925,7 +3416,7 @@ static void write_libraries(WriteData *wd, Main *main) BlendWriter writer = {wd}; writestruct(wd, ID_LI, Library, 1, main->curlib); - write_iddata(&writer, &main->curlib->id); + BKE_id_blend_write(&writer, &main->curlib->id); if (main->curlib->packedfile) { PackedFile *pf = main->curlib->packedfile; @@ -4130,12 +3621,17 @@ static bool write_file_handle(Main *mainvar, memcpy(id_buffer, id, idtype_struct_size); ((ID *)id_buffer)->tag = 0; - /* Those listbase data change every time we add/remove an ID, and also often when renaming - * one (due to re-sorting). This avoids generating a lot of false 'is changed' detections - * between undo steps. */ + /* Those listbase data change every time we add/remove an ID, and also often when + * renaming one (due to re-sorting). This avoids generating a lot of false 'is changed' + * detections between undo steps. */ ((ID *)id_buffer)->prev = NULL; ((ID *)id_buffer)->next = NULL; + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + if (id_type->blend_write != NULL) { + id_type->blend_write(&writer, (ID *)id_buffer, id); + } + switch ((ID_Type)GS(id->name)) { case ID_WM: write_windowmanager(&writer, (wmWindowManager *)id_buffer, id); @@ -4170,9 +3666,6 @@ static bool write_file_handle(Main *mainvar, case ID_LA: write_light(&writer, (Light *)id_buffer, id); break; - case ID_LT: - write_lattice(&writer, (Lattice *)id_buffer, id); - break; case ID_VF: write_vfont(&writer, (VFont *)id_buffer, id); break; @@ -4212,9 +3705,6 @@ static bool write_file_handle(Main *mainvar, case ID_TE: write_texture(&writer, (Tex *)id_buffer, id); break; - case ID_ME: - write_mesh(&writer, (Mesh *)id_buffer, id); - break; case ID_PA: write_particlesettings(&writer, (ParticleSettings *)id_buffer, id); break; @@ -4251,6 +3741,10 @@ static bool write_file_handle(Main *mainvar, case ID_SIM: write_simulation(&writer, (Simulation *)id_buffer, id); break; + case ID_ME: + case ID_LT: + /* Do nothing, handled in IDTypeInfo callback. */ + break; case ID_LI: /* Do nothing, handled below - and should never be reached. */ BLI_assert(0); @@ -4409,7 +3903,8 @@ bool BLO_write_file(Main *mainvar, if (remap_mode != BLO_WRITE_PATH_REMAP_NONE) { if (remap_mode == BLO_WRITE_PATH_REMAP_RELATIVE) { - /* Make all relative as none of the existing paths can be relative in an unsaved document. */ + /* Make all relative as none of the existing paths can be relative in an unsaved document. + */ if (G.relbase_valid == false) { remap_mode = BLO_WRITE_PATH_REMAP_RELATIVE_ALL; } diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 7368e3d044d..d2b747aa68f 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -138,6 +138,8 @@ set(SRC tools/bmesh_bevel.h tools/bmesh_bisect_plane.c tools/bmesh_bisect_plane.h + tools/bmesh_boolean.cc + tools/bmesh_boolean.h tools/bmesh_decimate.h tools/bmesh_decimate_collapse.c tools/bmesh_decimate_dissolve.c @@ -204,6 +206,18 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) + + list(APPEND INC_SYS + ${GMP_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${GMP_LIBRARIES} + ) +endif() + blender_add_lib(bf_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") if(WITH_GTESTS) diff --git a/source/blender/bmesh/bmesh_tools.h b/source/blender/bmesh/bmesh_tools.h index ee53cb9804d..daecbac63ab 100644 --- a/source/blender/bmesh/bmesh_tools.h +++ b/source/blender/bmesh/bmesh_tools.h @@ -30,6 +30,7 @@ extern "C" { #include "tools/bmesh_beautify.h" #include "tools/bmesh_bevel.h" #include "tools/bmesh_bisect_plane.h" +#include "tools/bmesh_boolean.h" #include "tools/bmesh_decimate.h" #include "tools/bmesh_edgenet.h" #include "tools/bmesh_edgesplit.h" diff --git a/source/blender/bmesh/tools/bmesh_boolean.cc b/source/blender/bmesh/tools/bmesh_boolean.cc new file mode 100644 index 00000000000..5d410d60496 --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_boolean.cc @@ -0,0 +1,479 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bmesh + * + * Main functions for boolean on a #BMesh (used by the tool and modifier) + */ + +#include "BLI_array.hh" +#include "BLI_math.h" +#include "BLI_math_mpq.hh" +#include "BLI_mesh_boolean.hh" +#include "BLI_mesh_intersect.hh" + +#include "bmesh.h" +#include "bmesh_boolean.h" +#include "bmesh_edgesplit.h" + +namespace blender { +namespace meshintersect { + +#ifdef WITH_GMP + +/** Make a #blender::meshintersect::Mesh from #BMesh bm. + * We are given a triangulation of it from the caller via #looptris, + * which are looptris_tot triples of loops that together tessellate + * the faces of bm. + * Return a second #IMesh in *r_triangulated that has the triangulated + * mesh, with face "orig" fields that connect the triangles back to + * the faces in the returned (polygonal) mesh. + */ +static IMesh mesh_from_bm(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + IMesh *r_triangulated, + IMeshArena *arena) +{ + BLI_assert(r_triangulated != nullptr); + BM_mesh_elem_index_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + BM_mesh_elem_table_ensure(bm, BM_VERT | BM_EDGE | BM_FACE); + /* Account for triangulation and intersects. */ + const int estimate_num_outv = (3 * bm->totvert) / 2; + const int estimate_num_outf = 4 * bm->totface; + arena->reserve(estimate_num_outv, estimate_num_outf); + Array<const Vert *> vert(bm->totvert); + for (int v = 0; v < bm->totvert; ++v) { + BMVert *bmv = BM_vert_at_index(bm, v); + vert[v] = arena->add_or_find_vert(mpq3(bmv->co[0], bmv->co[1], bmv->co[2]), v); + } + Array<Face *> face(bm->totface); + constexpr int estimated_max_facelen = 100; + Vector<const Vert *, estimated_max_facelen> face_vert; + Vector<int, estimated_max_facelen> face_edge_orig; + for (int f = 0; f < bm->totface; ++f) { + BMFace *bmf = BM_face_at_index(bm, f); + int flen = bmf->len; + face_vert.clear(); + face_edge_orig.clear(); + BMLoop *l = bmf->l_first; + for (int i = 0; i < flen; ++i) { + const Vert *v = vert[BM_elem_index_get(l->v)]; + face_vert.append(v); + int e_index = BM_elem_index_get(l->e); + face_edge_orig.append(e_index); + l = l->next; + } + face[f] = arena->add_face(face_vert, f, face_edge_orig); + } + /* Now do the triangulation mesh. + * The loop_tris have accurate v and f members for the triangles, + * but their next and e pointers are not correct for the loops + * that start added-diagonal edges. */ + Array<Face *> tri_face(looptris_tot); + face_vert.resize(3); + face_edge_orig.resize(3); + for (int i = 0; i < looptris_tot; ++i) { + BMFace *bmf = looptris[i][0]->f; + int f = BM_elem_index_get(bmf); + for (int j = 0; j < 3; ++j) { + BMLoop *l = looptris[i][j]; + int v_index = BM_elem_index_get(l->v); + int e_index; + if (l->next->v == looptris[i][(j + 1) % 3]->v) { + e_index = BM_elem_index_get(l->e); + } + else { + e_index = NO_INDEX; + } + face_vert[j] = vert[v_index]; + face_edge_orig[j] = e_index; + } + tri_face[i] = arena->add_face(face_vert, f, face_edge_orig); + } + r_triangulated->set_faces(tri_face); + return IMesh(face); +} + +static bool bmvert_attached_to_wire(const BMVert *bmv) +{ + /* This is not quite right. It returns true if the only edges + * Attached to \a bmv are wire edges. TODO: iterate through edges + * attached to \a bmv and check #BM_edge_is_wire. */ + return BM_vert_is_wire(bmv); +} + +static bool face_has_verts_in_order(BMesh *bm, BMFace *bmf, const BMVert *v1, const BMVert *v2) +{ + BMIter liter; + BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf)); + while (l != NULL) { + if (l->v == v1 && l->next->v == v2) { + return true; + } + l = static_cast<BMLoop *>(BM_iter_step(&liter)); + } + return false; +} + +/** Use the unused _BM_ELEM_TAG_ALT #BMElem.hflag to mark geometry we will keep. */ +constexpr uint KEEP_FLAG = (1 << 6); + +/** + * Change #BMesh bm to have the mesh match m_out. Return true if there were any changes at all. + * Vertices, faces, and edges in the current bm that are not used in the output are killed, + * except we don't kill wire edges and we don't kill hidden geometry. + * Also, the #BM_ELEM_TAG header flag is set for those #BMEdge's that come from intersections + * resulting from the intersection needed by the Boolean operation. + */ +static bool apply_mesh_output_to_bmesh(BMesh *bm, IMesh &m_out) +{ + bool any_change = false; + + m_out.populate_vert(); + + /* Initially mark all existing verts as "don't keep", except hidden verts + * and verts attached to wire edges. */ + for (int v = 0; v < bm->totvert; ++v) { + BMVert *bmv = BM_vert_at_index(bm, v); + if (BM_elem_flag_test(bmv, BM_ELEM_HIDDEN) || bmvert_attached_to_wire(bmv)) { + BM_elem_flag_enable(bmv, KEEP_FLAG); + } + else { + BM_elem_flag_disable(bmv, KEEP_FLAG); + } + } + + /* Reuse old or make new #BMVert's, depending on if there's an orig or not. + * For those reused, mark them "keep". + * Store needed old #BMVert's in new_bmvs first, as the table may be unusable after + * creating a new #BMVert. */ + Array<BMVert *> new_bmvs(m_out.vert_size()); + for (int v : m_out.vert_index_range()) { + const Vert *vertp = m_out.vert(v); + int orig = vertp->orig; + if (orig != NO_INDEX) { + BLI_assert(orig >= 0 && orig < bm->totvert); + BMVert *bmv = BM_vert_at_index(bm, orig); + new_bmvs[v] = bmv; + BM_elem_flag_enable(bmv, KEEP_FLAG); + } + else { + new_bmvs[v] = NULL; + } + } + for (int v : m_out.vert_index_range()) { + const Vert *vertp = m_out.vert(v); + if (new_bmvs[v] == NULL) { + float co[3]; + const double3 &d_co = vertp->co; + for (int i = 0; i < 3; ++i) { + co[i] = static_cast<float>(d_co[i]); + } + BMVert *bmv = BM_vert_create(bm, co, NULL, BM_CREATE_NOP); + new_bmvs[v] = bmv; + BM_elem_flag_enable(bmv, KEEP_FLAG); + any_change = true; + } + } + + /* Initially mark all existing faces as "don't keep", except hidden faces. + * Also, save current #BMFace pointers as creating faces will disturb the table. */ + Array<BMFace *> old_bmfs(bm->totface); + for (int f = 0; f < bm->totface; ++f) { + BMFace *bmf = BM_face_at_index(bm, f); + old_bmfs[f] = bmf; + if (BM_elem_flag_test(bmf, BM_ELEM_HIDDEN)) { + BM_elem_flag_enable(bmf, KEEP_FLAG); + } + else { + BM_elem_flag_disable(bmf, KEEP_FLAG); + } + } + + /* Save the original #BMEdge's so we can use them as examples. */ + Array<BMEdge *> old_edges(bm->totedge); + std::copy(bm->etable, bm->etable + bm->totedge, old_edges.begin()); + + /* Reuse or make new #BMFace's, as the faces are identical to old ones or not. + * If reusing, mark them as "keep". First find the maximum face length + * so we can declare some arrays outside of the face-creating loop. */ + int maxflen = 0; + for (const Face *f : m_out.faces()) { + maxflen = max_ii(maxflen, f->size()); + } + Array<BMVert *> face_bmverts(maxflen); + Array<BMEdge *> face_bmedges(maxflen); + for (const Face *f : m_out.faces()) { + const Face &face = *f; + int flen = face.size(); + for (int i = 0; i < flen; ++i) { + const Vert *v = face[i]; + int v_index = m_out.lookup_vert(v); + BLI_assert(v_index < new_bmvs.size()); + face_bmverts[i] = new_bmvs[v_index]; + } + BMFace *bmf = BM_face_exists(face_bmverts.data(), flen); + /* #BM_face_exists checks if the face exists with the vertices in either order. + * We can only reuse the face if the orientations are the same. */ + if (bmf != NULL && face_has_verts_in_order(bm, bmf, face_bmverts[0], face_bmverts[1])) { + BM_elem_flag_enable(bmf, KEEP_FLAG); + } + else { + int orig = face.orig; + BMFace *orig_face; + /* There should always be an orig face, but just being extra careful here. */ + if (orig != NO_INDEX) { + orig_face = old_bmfs[orig]; + } + else { + orig_face = NULL; + } + /* Make or find #BMEdge's. */ + for (int i = 0; i < flen; ++i) { + BMVert *bmv1 = face_bmverts[i]; + BMVert *bmv2 = face_bmverts[(i + 1) % flen]; + BMEdge *bme = BM_edge_exists(bmv1, bmv2); + if (bme == NULL) { + BMEdge *orig_edge = NULL; + if (face.edge_orig[i] != NO_INDEX) { + orig_edge = old_edges[face.edge_orig[i]]; + } + bme = BM_edge_create(bm, bmv1, bmv2, orig_edge, BM_CREATE_NOP); + if (orig_edge != NULL) { + BM_elem_select_copy(bm, bme, orig_edge); + } + } + face_bmedges[i] = bme; + if (face.is_intersect[i]) { + BM_elem_flag_enable(bme, BM_ELEM_TAG); + } + else { + BM_elem_flag_disable(bme, BM_ELEM_TAG); + } + } + BMFace *bmf = BM_face_create( + bm, face_bmverts.data(), face_bmedges.data(), flen, orig_face, BM_CREATE_NOP); + if (orig_face != NULL) { + BM_elem_select_copy(bm, bmf, orig_face); + } + BM_elem_flag_enable(bmf, KEEP_FLAG); + /* Now do interpolation of loop data (e.g., UV's) using the example face. */ + if (orig_face != NULL) { + BMIter liter; + BMLoop *l = static_cast<BMLoop *>(BM_iter_new(&liter, bm, BM_LOOPS_OF_FACE, bmf)); + while (l != NULL) { + BM_loop_interp_from_face(bm, l, orig_face, true, true); + l = static_cast<BMLoop *>(BM_iter_step(&liter)); + } + } + any_change = true; + } + } + + /* Now kill the unused faces and verts, and clear flags for kept ones. */ + /* #BM_ITER_MESH_MUTABLE macro needs type casts for C++, so expand here. + * TODO(howard): make some nice C++ iterators for #BMesh. */ + BMIter iter; + BMFace *bmf = static_cast<BMFace *>(BM_iter_new(&iter, bm, BM_FACES_OF_MESH, NULL)); + while (bmf != NULL) { +# ifdef DEBUG + iter.count = BM_iter_mesh_count(BM_FACES_OF_MESH, bm); +# endif + BMFace *bmf_next = static_cast<BMFace *>(BM_iter_step(&iter)); + if (BM_elem_flag_test(bmf, KEEP_FLAG)) { + BM_elem_flag_disable(bmf, KEEP_FLAG); + } + else { + BM_face_kill_loose(bm, bmf); +# if 0 + BM_face_kill(bm, bmf); +# endif + any_change = true; + } + bmf = bmf_next; + } + BMVert *bmv = static_cast<BMVert *>(BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, NULL)); + while (bmv != NULL) { +# ifdef DEBUG + iter.count = BM_iter_mesh_count(BM_VERTS_OF_MESH, bm); +# endif + BMVert *bmv_next = static_cast<BMVert *>(BM_iter_step(&iter)); + if (BM_elem_flag_test(bmv, KEEP_FLAG)) { + BM_elem_flag_disable(bmv, KEEP_FLAG); + } + else { + BM_vert_kill(bm, bmv); + any_change = true; + } + bmv = bmv_next; + } + + return any_change; +} + +static bool bmesh_boolean(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const bool use_separate_all, + const BoolOpType boolean_mode) +{ + IMeshArena arena; + IMesh m_triangulated; + IMesh m_in = mesh_from_bm(bm, looptris, looptris_tot, &m_triangulated, &arena); + std::function<int(int)> shape_fn; + int nshapes; + if (use_self) { + /* Unary boolean operation. Want every face where test_fn doesn't return -1. */ + nshapes = 1; + shape_fn = [bm, test_fn, user_data](int f) { + BMFace *bmf = BM_face_at_index(bm, f); + if (test_fn(bmf, user_data) != -1) { + return 0; + } + return -1; + }; + } + else { + nshapes = 2; + shape_fn = [bm, test_fn, user_data](int f) { + BMFace *bmf = BM_face_at_index(bm, f); + int test_val = test_fn(bmf, user_data); + if (test_val == 0) { + return 0; + } + if (test_val == 1) { + return 1; + } + return -1; + }; + } + IMesh m_out = boolean_mesh( + m_in, boolean_mode, nshapes, shape_fn, use_self, &m_triangulated, &arena); + bool any_change = apply_mesh_output_to_bmesh(bm, m_out); + if (use_separate_all) { + /* We are supposed to separate all faces that are incident on intersection edges. */ + BM_mesh_edgesplit(bm, false, true, false); + } + return any_change; +} + +#endif // WITH_GMP + +} // namespace meshintersect +} // namespace blender + +extern "C" { +/** + * Perform the boolean operation specified by boolean_mode on the mesh bm. + * The inputs to the boolean operation are either one sub-mesh (if use_self is true), + * or two sub-meshes. The sub-meshes are specified by providing a test_fn which takes + * a face and the supplied user_data and says with 'side' of the boolean operation + * that face is for: 0 for the first side (side A), 1 for the second side (side B), + * and -1 if the face is to be ignored completely in the boolean operation. + * + * If use_self is true, all operations do the same: the sub-mesh is self-intersected + * and all pieces inside that result are removed. + * Otherwise, the operations can be one of #BMESH_ISECT_BOOLEAN_ISECT, #BMESH_ISECT_BOOLEAN_UNION, + * or #BMESH_ISECT_BOOLEAN_DIFFERENCE. + * + * (The actual library function called to do the boolean is internally capable of handling + * n-ary operands, so maybe in the future we can expose that functionality to users.) + */ +#ifdef WITH_GMP +bool BM_mesh_boolean(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const int boolean_mode) +{ + return blender::meshintersect::bmesh_boolean( + bm, + looptris, + looptris_tot, + test_fn, + user_data, + use_self, + false, + static_cast<blender::meshintersect::BoolOpType>(boolean_mode)); +} + +/** + * Perform a Knife Intersection operation on the mesh bm. + * There are either one or two operands, the same as described above for BM_mesh_boolean(). + * If use_separate_all is true, each edge that is created from the intersection should + * be used to separate all its incident faces. TODO: implement that. + * TODO: need to ensure that "selected/non-selected" flag of original faces gets propagated + * to the intersection result faces. + */ +bool BM_mesh_boolean_knife(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const bool use_separate_all) +{ + return blender::meshintersect::bmesh_boolean(bm, + looptris, + looptris_tot, + test_fn, + user_data, + use_self, + use_separate_all, + blender::meshintersect::BoolOpType::None); +} +#else +bool BM_mesh_boolean(BMesh *UNUSED(bm), + struct BMLoop *(*looptris)[3], + const int UNUSED(looptris_tot), + int (*test_fn)(BMFace *, void *), + void *UNUSED(user_data), + const bool UNUSED(use_self), + const int UNUSED(boolean_mode)) +{ + UNUSED_VARS(looptris, test_fn); + return false; +} + +/** + * Perform a Knife Intersection operation on the mesh bm. + * There are either one or two operands, the same as described above for #BM_mesh_boolean(). + * If use_separate_all is true, each edge that is created from the intersection should + * be used to separate all its incident faces. TODO: implement that. + * TODO: need to ensure that "selected/non-selected" flag of original faces gets propagated + * to the intersection result faces. + */ +bool BM_mesh_boolean_knife(BMesh *UNUSED(bm), + struct BMLoop *(*looptris)[3], + const int UNUSED(looptris_tot), + int (*test_fn)(BMFace *, void *), + void *UNUSED(user_data), + const bool UNUSED(use_self), + const bool UNUSED(use_separate_all)) +{ + UNUSED_VARS(looptris, test_fn); + return false; +} +#endif + +} /* extern "C" */ diff --git a/source/blender/bmesh/tools/bmesh_boolean.h b/source/blender/bmesh/tools/bmesh_boolean.h new file mode 100644 index 00000000000..d1b4fb2509c --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_boolean.h @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bmesh + */ + +#ifdef __cplusplus +extern "C" { +#endif + +bool BM_mesh_boolean(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const int boolean_mode); + +bool BM_mesh_boolean_knife(BMesh *bm, + struct BMLoop *(*looptris)[3], + const int looptris_tot, + int (*test_fn)(BMFace *f, void *user_data), + void *user_data, + const bool use_self, + const bool use_separate_all); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/bmesh/tools/bmesh_decimate_collapse.c b/source/blender/bmesh/tools/bmesh_decimate_collapse.c index a2a949b5567..8d4c831cbc1 100644 --- a/source/blender/bmesh/tools/bmesh_decimate_collapse.c +++ b/source/blender/bmesh/tools/bmesh_decimate_collapse.c @@ -117,7 +117,7 @@ static void bm_decim_build_quadrics(BMesh *bm, Quadric *vquadrics) cross_v3_v3v3(edge_plane, edge_vector, f->no); copy_v3db_v3fl(edge_plane_db, edge_plane); - if (normalize_v3_d(edge_plane_db) > (double)FLT_EPSILON) { + if (normalize_v3_db(edge_plane_db) > (double)FLT_EPSILON) { Quadric q; float center[3]; diff --git a/source/blender/bmesh/tools/bmesh_edgesplit.h b/source/blender/bmesh/tools/bmesh_edgesplit.h index 4b8c07fc992..4d3db67ef5f 100644 --- a/source/blender/bmesh/tools/bmesh_edgesplit.h +++ b/source/blender/bmesh/tools/bmesh_edgesplit.h @@ -20,7 +20,15 @@ * \ingroup bmesh */ +#ifdef __cplusplus +extern "C" { +#endif + void BM_mesh_edgesplit(BMesh *bm, const bool use_verts, const bool tag_only, const bool copy_select); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/depsgraph/DEG_depsgraph.h b/source/blender/depsgraph/DEG_depsgraph.h index 8f33e9f480d..0ded511b8f8 100644 --- a/source/blender/depsgraph/DEG_depsgraph.h +++ b/source/blender/depsgraph/DEG_depsgraph.h @@ -127,6 +127,12 @@ void DEG_graph_id_tag_update(struct Main *bmain, struct ID *id, int flag); +/* Tag all dependency graphs when time has changed. */ +void DEG_time_tag_update(struct Main *bmain); + +/* Tag a dependency graph when time has changed. */ +void DEG_graph_time_tag_update(struct Depsgraph *depsgraph); + /* Mark a particular datablock type as having changing. This does * not cause any updates but is used by external render engines to detect if for * example a datablock was removed. */ @@ -155,8 +161,6 @@ void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime); /* Data changed recalculation entry point. */ void DEG_evaluate_on_refresh(Depsgraph *graph); -bool DEG_needs_eval(Depsgraph *graph); - /* Editors Integration -------------------------- */ /* Mechanism to allow editors to be informed of depsgraph updates, diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index e262c880421..670827dc4d8 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -1129,9 +1129,11 @@ void DepsgraphNodeBuilder::build_rigidbody(Scene *scene) if (object->rigidbody_object == nullptr) { continue; } - if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) { + + if (!BKE_rigidbody_is_affected_by_simulation(object)) { continue; } + /* Create operation for flushing results. */ /* Object's transform component - where the rigidbody operation * lives. */ @@ -1828,7 +1830,7 @@ void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene) function_bind(BKE_scene_eval_sequencer_sequences, _1, scene_cow)); /* Make sure data for sequences is in the graph. */ Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { build_idproperties(seq->prop); if (seq->sound != nullptr) { build_sound(seq->sound); @@ -1845,7 +1847,7 @@ void DepsgraphNodeBuilder::build_scene_sequencer(Scene *scene) } /* TODO(sergey): Movie clip, scene, camera, mask. */ } - SEQ_END; + SEQ_ALL_END; } void DepsgraphNodeBuilder::build_scene_audio(Scene *scene) diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index c0b692f28c1..5f637a92069 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1703,9 +1703,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) if (object->rigidbody_object == nullptr) { continue; } - if (object->rigidbody_object->type == RBO_TYPE_PASSIVE) { - continue; - } if (object->parent != nullptr && object->parent->rigidbody_object != nullptr && object->parent->rigidbody_object->shape == RB_SHAPE_COMPOUND) { @@ -1716,10 +1713,6 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) continue; } - OperationKey rb_transform_copy_key( - &object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - /* Rigid body synchronization depends on the actual simulation. */ - add_relation(rb_simulate_key, rb_transform_copy_key, "Rigidbody Sim Eval -> RBO Sync"); /* Simulation uses object transformation after parenting and solving constraints. */ OperationKey object_transform_simulation_init_key( &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_SIMULATION_INIT); @@ -1737,47 +1730,27 @@ void DepsgraphRelationBuilder::build_rigidbody(Scene *scene) ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY); add_relation(object_geometry_key, rb_simulate_key, - "Object Geom Eval -> Rigidbody Rebuild", + "Object Geom Eval -> Rigidbody Sim Eval", RELATION_FLAG_GODMODE); } + /* Final transform is whetever solver gave to us. */ - OperationKey object_transform_final_key( - &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); - add_relation( - rb_transform_copy_key, object_transform_final_key, "Rigidbody Sync -> Transform Final"); - } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - } - /* Constraints. */ - if (rbw->constraints != nullptr) { - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (rbw->constraints, object) { - RigidBodyCon *rbc = object->rigidbody_constraint; - if (rbc == nullptr || rbc->ob1 == nullptr || rbc->ob2 == nullptr) { - /* When either ob1 or ob2 is nullptr, the constraint doesn't - * work. */ - continue; - } - /* Make sure indirectly linked objects are fully built. */ - build_object(object); - build_object(rbc->ob1); - build_object(rbc->ob2); - /* final result of the constraint object's transform controls how - * the constraint affects the physics sim for these objects. */ - ComponentKey trans_key(&object->id, NodeType::TRANSFORM); - if (rbc->ob1->rigidbody_object->type == RBO_TYPE_ACTIVE) { - OperationKey ob1_key( - &rbc->ob1->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - /* Constrained-objects sync depends on the constraint-holder. */ - add_relation(trans_key, ob1_key, "RigidBodyConstraint -> RBC.Object_1"); - } - if (rbc->ob2->rigidbody_object->type == RBO_TYPE_ACTIVE) { - OperationKey ob2_key( - &rbc->ob2->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); - /* Constrained-objects sync depends on the constraint-holder. */ - add_relation(trans_key, ob2_key, "RigidBodyConstraint -> RBC.Object_2"); + if (BKE_rigidbody_is_affected_by_simulation(object)) { + /* We do not have to update the objects final transform after the simulation if it is + * passive or controlled by the animation system in blender. + * (Bullet doesn't move the object at all in these cases) + */ + OperationKey rb_transform_copy_key( + &object->id, NodeType::TRANSFORM, OperationCode::RIGIDBODY_TRANSFORM_COPY); + /* Rigid body synchronization depends on the actual simulation. */ + add_relation(rb_simulate_key, rb_transform_copy_key, "Rigidbody Sim Eval -> RBO Sync"); + + OperationKey object_transform_final_key( + &object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_FINAL); + add_relation(rb_transform_copy_key, + object_transform_final_key, + "Rigidbody Sync -> Transform Final"); } - /* Ensure that sim depends on this constraint's transform. */ - add_relation(trans_key, rb_simulate_key, "RigidBodyConstraint Transform -> RB Simulation"); } FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } @@ -2688,7 +2661,7 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) ComponentKey sequencer_key(&scene->id, NodeType::SEQUENCER); Sequence *seq; bool has_audio_strips = false; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { build_idproperties(seq->prop); if (seq->sound != nullptr) { build_sound(seq->sound); @@ -2714,7 +2687,7 @@ void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene) } /* TODO(sergey): Movie clip, camera, mask. */ } - SEQ_END; + SEQ_ALL_END; if (has_audio_strips) { add_relation(sequencer_key, scene_audio_key, "Sequencer -> Audio"); } diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 4a9c840dd9f..99804a7cd7d 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -63,7 +63,6 @@ namespace deg { Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode) : time_source(nullptr), need_update(true), - need_update_time(false), bmain(bmain), scene(scene), view_layer(view_layer), @@ -78,6 +77,8 @@ Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluati memset(id_type_updated, 0, sizeof(id_type_updated)); memset(id_type_exist, 0, sizeof(id_type_exist)); memset(physics_relations, 0, sizeof(physics_relations)); + + add_time_source(); } Depsgraph::~Depsgraph() @@ -103,6 +104,11 @@ TimeSourceNode *Depsgraph::find_time_source() const return time_source; } +void Depsgraph::tag_time_source() +{ + time_source->tag_update(this, DEG_UPDATE_SOURCE_TIME); +} + IDNode *Depsgraph::find_id_node(const ID *id) const { return id_hash.lookup_default(id, nullptr); diff --git a/source/blender/depsgraph/intern/depsgraph.h b/source/blender/depsgraph/intern/depsgraph.h index ea579a4958e..e03846f81e2 100644 --- a/source/blender/depsgraph/intern/depsgraph.h +++ b/source/blender/depsgraph/intern/depsgraph.h @@ -68,6 +68,7 @@ struct Depsgraph { TimeSourceNode *add_time_source(); TimeSourceNode *find_time_source() const; + void tag_time_source(); IDNode *find_id_node(const ID *id) const; IDNode *add_id_node(ID *id, ID *id_cow_hint = nullptr); @@ -121,10 +122,6 @@ struct Depsgraph { /* Nodes which have been tagged as "directly modified". */ Set<OperationNode *> entry_tags; - /* Special entry tag for time source. Allows to tag invisible dependency graphs for update when - * scene frame changes, so then when dependency graph becomes visible it is on a proper state. */ - bool need_update_time; - /* Convenience Data ................... */ /* XXX: should be collected after building (if actually needed?) */ diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index 0c116f5863c..1ad3fdbc9da 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -47,38 +47,37 @@ namespace deg = blender::deg; -/* Evaluate all nodes tagged for updating. */ -void DEG_evaluate_on_refresh(Depsgraph *graph) +static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph) { - deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - deg_graph->ctime = BKE_scene_frame_get(deg_graph->scene); - /* Update time in scene. */ + /* Update the time on the cow scene. */ if (deg_graph->scene_cow) { BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime); } + deg::deg_graph_flush_updates(deg_graph); deg::deg_evaluate_on_refresh(deg_graph); - deg_graph->need_update_time = false; } -/* Frame-change happened for root scene that graph belongs to. */ -void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime) +/* Evaluate all nodes tagged for updating. */ +void DEG_evaluate_on_refresh(Depsgraph *graph) { deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - deg_graph->ctime = ctime; - deg_graph->need_update_time = true; - deg::deg_graph_flush_updates(deg_graph); - /* Update time in scene. */ - if (deg_graph->scene_cow) { - BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->ctime); + const Scene *scene = DEG_get_input_scene(graph); + const float ctime = BKE_scene_frame_get(scene); + + if (ctime != deg_graph->ctime) { + deg_graph->tag_time_source(); + deg_graph->ctime = ctime; } - /* Perform recalculation updates. */ - deg::deg_evaluate_on_refresh(deg_graph); - deg_graph->need_update_time = false; + + deg_flush_updates_and_refresh(deg_graph); } -bool DEG_needs_eval(Depsgraph *graph) +/* Frame-change happened for root scene that graph belongs to. */ +void DEG_evaluate_on_framechange(Depsgraph *graph, float ctime) { deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph); - return !deg_graph->entry_tags.is_empty() || deg_graph->need_update_time; + deg_graph->tag_time_source(); + deg_graph->ctime = ctime; + deg_flush_updates_and_refresh(deg_graph); } diff --git a/source/blender/depsgraph/intern/depsgraph_tag.cc b/source/blender/depsgraph/intern/depsgraph_tag.cc index 4a2d47f9379..868f88d8fcd 100644 --- a/source/blender/depsgraph/intern/depsgraph_tag.cc +++ b/source/blender/depsgraph/intern/depsgraph_tag.cc @@ -66,6 +66,7 @@ #include "intern/node/deg_node_factory.h" #include "intern/node/deg_node_id.h" #include "intern/node/deg_node_operation.h" +#include "intern/node/deg_node_time.h" namespace deg = blender::deg; @@ -230,9 +231,6 @@ void depsgraph_tag_to_component_opcode(const ID *id, case ID_RECALC_SOURCE: *component_type = NodeType::PARAMETERS; break; - case ID_RECALC_TIME: - BLI_assert(!"Should be handled outside of this function"); - break; case ID_RECALC_ALL: case ID_RECALC_PSYS_ALL: BLI_assert(!"Should not happen"); @@ -372,12 +370,6 @@ void graph_id_tag_update_single_flag(Main *bmain, } return; } - if (tag == ID_RECALC_TIME) { - if (graph != nullptr) { - graph->need_update_time = true; - } - return; - } /* Get description of what is to be tagged. */ NodeType component_type; OperationCode operation_code; @@ -462,8 +454,8 @@ const char *update_source_as_string(eUpdateSource source) int deg_recalc_flags_for_legacy_zero() { - return ID_RECALC_ALL & ~(ID_RECALC_PSYS_ALL | ID_RECALC_ANIMATION | ID_RECALC_SOURCE | - ID_RECALC_TIME | ID_RECALC_EDITORS); + return ID_RECALC_ALL & + ~(ID_RECALC_PSYS_ALL | ID_RECALC_ANIMATION | ID_RECALC_SOURCE | ID_RECALC_EDITORS); } int deg_recalc_flags_effective(Depsgraph *graph, int flags) @@ -734,8 +726,6 @@ const char *DEG_update_tag_as_string(IDRecalcFlag flag) return "AUDIO"; case ID_RECALC_PARAMETERS: return "PARAMETERS"; - case ID_RECALC_TIME: - return "TIME"; case ID_RECALC_SOURCE: return "SOURCE"; case ID_RECALC_ALL: @@ -772,6 +762,19 @@ void DEG_graph_id_tag_update(struct Main *bmain, deg::graph_id_tag_update(bmain, graph, id, flag, deg::DEG_UPDATE_SOURCE_USER_EDIT); } +void DEG_time_tag_update(struct Main *bmain) +{ + for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) { + DEG_graph_time_tag_update(reinterpret_cast<::Depsgraph *>(depsgraph)); + } +} + +void DEG_graph_time_tag_update(struct Depsgraph *depsgraph) +{ + deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph); + deg_graph->tag_time_source(); +} + /* Mark a particular datablock type as having changing. */ void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type) { diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc index dea23c9f96d..5ccdcbec858 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc @@ -357,14 +357,9 @@ void deg_graph_flush_updates(Depsgraph *graph) BLI_assert(graph != nullptr); Main *bmain = graph->bmain; + graph->time_source->flush_update_tag(graph); + /* Nothing to update, early out. */ - if (graph->need_update_time) { - const Scene *scene_orig = graph->scene; - const float ctime = BKE_scene_frame_get(scene_orig); - TimeSourceNode *time_source = graph->find_time_source(); - graph->ctime = ctime; - time_source->tag_update(graph, DEG_UPDATE_SOURCE_TIME); - } if (graph->entry_tags.is_empty()) { return; } @@ -412,6 +407,8 @@ void deg_graph_clear_tags(Depsgraph *graph) } /* Clear any entry tags which haven't been flushed. */ graph->entry_tags.clear(); + + graph->time_source->tagged_for_update = false; } } // namespace deg diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc index ba7d20c0ba8..4d79480a5ad 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup_sequencer.cc @@ -41,7 +41,7 @@ SequencerBackup::SequencerBackup(const Depsgraph *depsgraph) : depsgraph(depsgra void SequencerBackup::init_from_scene(Scene *scene) { Sequence *sequence; - SEQ_BEGIN (scene->ed, sequence) { + SEQ_ALL_BEGIN (scene->ed, sequence) { SequenceBackup sequence_backup(depsgraph); sequence_backup.init_from_sequence(sequence); if (!sequence_backup.isEmpty()) { @@ -50,13 +50,13 @@ void SequencerBackup::init_from_scene(Scene *scene) sequences_backup.add(session_uuid, sequence_backup); } } - SEQ_END; + SEQ_ALL_END; } void SequencerBackup::restore_to_scene(Scene *scene) { Sequence *sequence; - SEQ_BEGIN (scene->ed, sequence) { + SEQ_ALL_BEGIN (scene->ed, sequence) { const SessionUUID &session_uuid = sequence->runtime.session_uuid; BLI_assert(BLI_session_uuid_is_generated(&session_uuid)); SequenceBackup *sequence_backup = sequences_backup.lookup_ptr(session_uuid); @@ -64,7 +64,7 @@ void SequencerBackup::restore_to_scene(Scene *scene) sequence_backup->restore_to_sequence(sequence); } } - SEQ_END; + SEQ_ALL_END; /* Cleanup audio while the scene is still known. */ for (SequenceBackup &sequence_backup : sequences_backup.values()) { if (sequence_backup.scene_sound != nullptr) { diff --git a/source/blender/depsgraph/intern/node/deg_node_time.cc b/source/blender/depsgraph/intern/node/deg_node_time.cc index af931fbae34..4f7f70fef33 100644 --- a/source/blender/depsgraph/intern/node/deg_node_time.cc +++ b/source/blender/depsgraph/intern/node/deg_node_time.cc @@ -31,8 +31,16 @@ namespace blender { namespace deg { -void TimeSourceNode::tag_update(Depsgraph *graph, eUpdateSource /*source*/) +void TimeSourceNode::tag_update(Depsgraph * /*graph*/, eUpdateSource /*source*/) { + tagged_for_update = true; +} + +void TimeSourceNode::flush_update_tag(Depsgraph *graph) +{ + if (!tagged_for_update) { + return; + } for (Relation *rel : outlinks) { Node *node = rel->to; node->tag_update(graph, DEG_UPDATE_SOURCE_TIME); diff --git a/source/blender/depsgraph/intern/node/deg_node_time.h b/source/blender/depsgraph/intern/node/deg_node_time.h index fe17684abb0..79ad92f336f 100644 --- a/source/blender/depsgraph/intern/node/deg_node_time.h +++ b/source/blender/depsgraph/intern/node/deg_node_time.h @@ -30,10 +30,14 @@ namespace deg { /* Time Source Node. */ struct TimeSourceNode : public Node { + bool tagged_for_update = false; + // TODO: evaluate() operation needed virtual void tag_update(Depsgraph *graph, eUpdateSource source) override; + void flush_update_tag(Depsgraph *graph); + DEG_DEPSNODE_DECLARE; }; diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index f85b03dc517..83d39b606fe 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -419,6 +419,28 @@ if(WITH_XR_OPENXR) add_definitions(-DWITH_XR_OPENXR) endif() +if(WITH_GTESTS) + if(WITH_OPENGL_DRAW_TESTS) + add_definitions(-DWITH_OPENGL_DRAW_TESTS) + endif() +endif() + add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_draw "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WITH_GTESTS) + if(WITH_OPENGL_DRAW_TESTS) + set(TEST_SRC + tests/shaders_test.cc + ) + set(TEST_INC + "../../../intern/ghost/" + ) + set(TEST_LIB + bf_draw + ) + include(GTestTesting) + blender_add_test_lib(bf_draw_tests "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${LIB};${TEST_LIB}") + endif() +endif()
\ No newline at end of file diff --git a/source/blender/draw/engines/eevee/eevee_data.c b/source/blender/draw/engines/eevee/eevee_data.c index e18c43fc643..5c4ee015c86 100644 --- a/source/blender/draw/engines/eevee/eevee_data.c +++ b/source/blender/draw/engines/eevee/eevee_data.c @@ -254,7 +254,7 @@ EEVEE_ViewLayerData *EEVEE_view_layer_data_get(void) static void eevee_view_layer_init(EEVEE_ViewLayerData *sldata) { - sldata->common_ubo = DRW_uniformbuffer_create(sizeof(sldata->common_data), NULL); + sldata->common_ubo = GPU_uniformbuf_create(sizeof(sldata->common_data)); } EEVEE_ViewLayerData *EEVEE_view_layer_data_ensure_ex(struct ViewLayer *view_layer) diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 72f008ea66a..6ab267ceb03 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -267,7 +267,7 @@ static void eevee_draw_scene(void *vedata) /* Set ray type. */ sldata->common_data.ray_type = EEVEE_RAY_CAMERA; sldata->common_data.ray_depth = 0.0f; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); GPU_framebuffer_bind(fbl->main_fb); eGPUFrameBufferBits clear_bits = GPU_DEPTH_BIT; diff --git a/source/blender/draw/engines/eevee/eevee_lightcache.c b/source/blender/draw/engines/eevee/eevee_lightcache.c index a785d27c2db..088a08fb51a 100644 --- a/source/blender/draw/engines/eevee/eevee_lightcache.c +++ b/source/blender/draw/engines/eevee/eevee_lightcache.c @@ -942,7 +942,7 @@ static void eevee_lightbake_render_world_sample(void *ved, void *user_data) sldata->common_data.ray_type = EEVEE_RAY_GLOSSY; sldata->common_data.ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb); EEVEE_lightbake_filter_glossy(sldata, vedata, @@ -956,7 +956,7 @@ static void eevee_lightbake_render_world_sample(void *ved, void *user_data) sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE; sldata->common_data.ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_world(sldata, vedata, lbake->rt_fb); EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake->rt_color, lbake->store_fb, 0, 1.0f); @@ -1079,7 +1079,7 @@ static void eevee_lightbake_render_grid_sample(void *ved, void *user_data) if (lbake->bounce_curr == 0) { common_data->prb_num_render_grid = 0; } - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_scene(sldata, vedata, lbake->rt_fb, pos, prb->clipsta, prb->clipend); @@ -1145,7 +1145,7 @@ static void eevee_lightbake_render_probe_sample(void *ved, void *user_data) common_data->prb_num_render_cube = 0; common_data->ray_type = EEVEE_RAY_GLOSSY; common_data->ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_scene( sldata, vedata, lbake->rt_fb, eprobe->position, prb->clipsta, prb->clipend); @@ -1430,7 +1430,7 @@ void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata, sldata->common_data.ray_type = EEVEE_RAY_GLOSSY; sldata->common_data.ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb); EEVEE_lightbake_filter_glossy(sldata, vedata, @@ -1444,7 +1444,7 @@ void EEVEE_lightbake_update_world_quick(EEVEE_ViewLayerData *sldata, sldata->common_data.ray_type = EEVEE_RAY_DIFFUSE; sldata->common_data.ray_depth = 1; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); EEVEE_lightbake_render_world(sldata, vedata, lbake.rt_fb); EEVEE_lightbake_filter_diffuse(sldata, vedata, lbake.rt_color, lbake.store_fb, 0, 1.0f); diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 0f4a9dc79b6..63cc07c321e 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -39,6 +39,7 @@ #include "GPU_extensions.h" #include "GPU_material.h" #include "GPU_texture.h" +#include "GPU_uniform_buffer.h" #include "DEG_depsgraph_query.h" @@ -203,10 +204,9 @@ void EEVEE_lightprobes_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) if (!sldata->probes) { sldata->probes = MEM_callocN(sizeof(EEVEE_LightProbesInfo), "EEVEE_LightProbesInfo"); - sldata->probe_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightProbe) * MAX_PROBE, NULL); - sldata->grid_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_LightGrid) * MAX_GRID, NULL); - sldata->planar_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR, - NULL); + sldata->probe_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightProbe) * MAX_PROBE); + sldata->grid_ubo = GPU_uniformbuf_create(sizeof(EEVEE_LightGrid) * MAX_GRID); + sldata->planar_ubo = GPU_uniformbuf_create(sizeof(EEVEE_PlanarReflection) * MAX_PLANAR); } common_data->prb_num_planar = 0; @@ -724,8 +724,8 @@ void EEVEE_lightprobes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *ved eevee_lightprobes_extract_from_cache(sldata->probes, light_cache); - DRW_uniformbuffer_update(sldata->probe_ubo, &sldata->probes->probe_data); - DRW_uniformbuffer_update(sldata->grid_ubo, &sldata->probes->grid_data); + GPU_uniformbuf_update(sldata->probe_ubo, &sldata->probes->probe_data); + GPU_uniformbuf_update(sldata->grid_ubo, &sldata->probes->grid_data); /* For shading, save max level of the octahedron map */ sldata->common_data.prb_lod_cube_max = (float)light_cache->mips_len; @@ -1241,7 +1241,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ray_type = EEVEE_RAY_GLOSSY; common_data->ray_depth = 1.0f; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); /* Rendering happens here! */ eevee_lightbake_render_scene_to_planars(sldata, vedata); @@ -1249,7 +1249,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v /* Make sure no additional visibility check runs after this. */ pinfo->vis_data.collection = NULL; - DRW_uniformbuffer_update(sldata->planar_ubo, &sldata->probes->planar_data); + GPU_uniformbuf_update(sldata->planar_ubo, &sldata->probes->planar_data); /* Restore */ common_data->prb_num_planar = pinfo->num_planar; diff --git a/source/blender/draw/engines/eevee/eevee_lights.c b/source/blender/draw/engines/eevee/eevee_lights.c index c6e8bac0949..b7112c07cab 100644 --- a/source/blender/draw/engines/eevee/eevee_lights.c +++ b/source/blender/draw/engines/eevee/eevee_lights.c @@ -214,5 +214,5 @@ void EEVEE_lights_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *UNUSED(v sldata->common_data.la_num_light = linfo->num_light; - DRW_uniformbuffer_update(sldata->light_ubo, &linfo->light_data); + GPU_uniformbuf_update(sldata->light_ubo, &linfo->light_data); } diff --git a/source/blender/draw/engines/eevee/eevee_lookdev.c b/source/blender/draw/engines/eevee/eevee_lookdev.c index f79d90500bd..6253203bab6 100644 --- a/source/blender/draw/engines/eevee/eevee_lookdev.c +++ b/source/blender/draw/engines/eevee/eevee_lookdev.c @@ -275,7 +275,7 @@ void EEVEE_lookdev_draw(EEVEE_Data *vedata) common->ao_dist = 0.0f; common->ao_factor = 0.0f; common->ao_settings = 0.0f; - DRW_uniformbuffer_update(sldata->common_ubo, common); + GPU_uniformbuf_update(sldata->common_ubo, common); /* override matrices */ float winmat[4][4], viewmat[4][4]; diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index fb07208be47..b6e20416dfb 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -243,33 +243,34 @@ void EEVEE_materials_init(EEVEE_ViewLayerData *sldata, { /* Create RenderPass UBO */ if (sldata->renderpass_ubo.combined == NULL) { - sldata->renderpass_ubo.combined = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){true, true, true, true, true, false, false}); - - sldata->renderpass_ubo.diff_color = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){true, false, false, false, false, true, false}); - - sldata->renderpass_ubo.diff_light = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){true, true, false, false, false, false, false}); - - sldata->renderpass_ubo.spec_color = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){false, false, true, false, false, false, false}); - - sldata->renderpass_ubo.spec_light = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){false, false, true, true, false, false, false}); - - sldata->renderpass_ubo.emit = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){false, false, false, false, true, false, false}); - - sldata->renderpass_ubo.environment = DRW_uniformbuffer_create( - sizeof(EEVEE_RenderPassData), - &(const EEVEE_RenderPassData){true, true, true, true, true, false, true}); + EEVEE_RenderPassData data; + data = (EEVEE_RenderPassData){true, true, true, true, true, false, false}; + sldata->renderpass_ubo.combined = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.combined"); + + data = (EEVEE_RenderPassData){true, false, false, false, false, true, false}; + sldata->renderpass_ubo.diff_color = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.diff_color"); + + data = (EEVEE_RenderPassData){true, true, false, false, false, false, false}; + sldata->renderpass_ubo.diff_light = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.diff_light"); + + data = (EEVEE_RenderPassData){false, false, true, false, false, false, false}; + sldata->renderpass_ubo.spec_color = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.spec_color"); + + data = (EEVEE_RenderPassData){false, false, true, true, false, false, false}; + sldata->renderpass_ubo.spec_light = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.spec_light"); + + data = (EEVEE_RenderPassData){false, false, false, false, true, false, false}; + sldata->renderpass_ubo.emit = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.emit"); + + data = (EEVEE_RenderPassData){true, true, true, true, true, false, true}; + sldata->renderpass_ubo.environment = GPU_uniformbuf_create_ex( + sizeof(data), &data, "renderpass_ubo.environment"); } /* Used combined pass by default. */ @@ -962,7 +963,7 @@ static void material_renderpass_accumulate(EEVEE_FramebufferList *fbl, DRWPass *renderpass, EEVEE_PrivateData *pd, GPUTexture *output_tx, - struct GPUUniformBuffer *renderpass_option_ubo) + struct GPUUniformBuf *renderpass_option_ubo) { GPU_framebuffer_texture_attach(fbl->material_accum_fb, output_tx, 0, 0); GPU_framebuffer_bind(fbl->material_accum_fb); diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index 40d7676c38c..b25f21ce929 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -32,6 +32,10 @@ #include "BKE_camera.h" +#ifdef __cplusplus +extern "C" { +#endif + struct EEVEE_ShadowCasterBuffer; struct GPUFrameBuffer; struct Object; @@ -818,9 +822,9 @@ typedef struct EEVEE_ViewLayerData { /* Lights */ struct EEVEE_LightsInfo *lights; - struct GPUUniformBuffer *light_ubo; - struct GPUUniformBuffer *shadow_ubo; - struct GPUUniformBuffer *shadow_samples_ubo; + struct GPUUniformBuf *light_ubo; + struct GPUUniformBuf *shadow_ubo; + struct GPUUniformBuf *shadow_samples_ubo; struct GPUFrameBuffer *shadow_fb; @@ -832,24 +836,24 @@ typedef struct EEVEE_ViewLayerData { /* Probes */ struct EEVEE_LightProbesInfo *probes; - struct GPUUniformBuffer *probe_ubo; - struct GPUUniformBuffer *grid_ubo; - struct GPUUniformBuffer *planar_ubo; + struct GPUUniformBuf *probe_ubo; + struct GPUUniformBuf *grid_ubo; + struct GPUUniformBuf *planar_ubo; /* Material Render passes */ struct { - struct GPUUniformBuffer *combined; - struct GPUUniformBuffer *environment; - struct GPUUniformBuffer *diff_color; - struct GPUUniformBuffer *diff_light; - struct GPUUniformBuffer *spec_color; - struct GPUUniformBuffer *spec_light; - struct GPUUniformBuffer *emit; + struct GPUUniformBuf *combined; + struct GPUUniformBuf *environment; + struct GPUUniformBuf *diff_color; + struct GPUUniformBuf *diff_light; + struct GPUUniformBuf *spec_color; + struct GPUUniformBuf *spec_light; + struct GPUUniformBuf *emit; } renderpass_ubo; /* Common Uniform Buffer */ struct EEVEE_CommonUniformBuffer common_data; - struct GPUUniformBuffer *common_ubo; + struct GPUUniformBuf *common_ubo; struct LightCache *fallback_lightcache; @@ -953,7 +957,7 @@ typedef struct EEVEE_PrivateData { GPUTexture *renderpass_col_input; GPUTexture *renderpass_light_input; /* Renderpass ubo reference used by material pass. */ - struct GPUUniformBuffer *renderpass_ubo; + struct GPUUniformBuf *renderpass_ubo; /** For rendering shadows. */ struct DRWView *cube_views[6]; /** For rendering probes. */ @@ -1363,3 +1367,7 @@ static const float cubefacemat[6][4][4] = { {0.0f, 0.0f, 1.0f, 0.0f}, {0.0f, 0.0f, 0.0f, 1.0f}}, }; + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/eevee/eevee_render.c b/source/blender/draw/engines/eevee/eevee_render.c index 65a856c39e1..2351b06db98 100644 --- a/source/blender/draw/engines/eevee/eevee_render.c +++ b/source/blender/draw/engines/eevee/eevee_render.c @@ -574,7 +574,7 @@ void EEVEE_render_draw(EEVEE_Data *vedata, RenderEngine *engine, RenderLayer *rl /* Set ray type. */ sldata->common_data.ray_type = EEVEE_RAY_CAMERA; sldata->common_data.ray_depth = 0.0f; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); GPU_framebuffer_bind(fbl->main_fb); GPU_framebuffer_clear_color_depth_stencil(fbl->main_fb, clear_col, clear_depth, clear_stencil); diff --git a/source/blender/draw/engines/eevee/eevee_shadows.c b/source/blender/draw/engines/eevee/eevee_shadows.c index 71a4da9fcab..79d89ab620c 100644 --- a/source/blender/draw/engines/eevee/eevee_shadows.c +++ b/source/blender/draw/engines/eevee/eevee_shadows.c @@ -71,8 +71,8 @@ void EEVEE_shadows_init(EEVEE_ViewLayerData *sldata) if (!sldata->lights) { sldata->lights = MEM_callocN(sizeof(EEVEE_LightsInfo), "EEVEE_LightsInfo"); - sldata->light_ubo = DRW_uniformbuffer_create(sizeof(EEVEE_Light) * MAX_LIGHT, NULL); - sldata->shadow_ubo = DRW_uniformbuffer_create(shadow_ubo_size, NULL); + sldata->light_ubo = GPU_uniformbuf_create_ex(sizeof(EEVEE_Light) * MAX_LIGHT, NULL, "evLight"); + sldata->shadow_ubo = GPU_uniformbuf_create_ex(shadow_ubo_size, NULL, "evShadow"); for (int i = 0; i < 2; i++) { sldata->shcasters_buffers[i].bbox = MEM_callocN( @@ -338,7 +338,7 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView if (any_visible) { sldata->common_data.ray_type = EEVEE_RAY_SHADOW; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); } DRW_stats_group_start("Cube Shadow Maps"); @@ -361,11 +361,11 @@ void EEVEE_shadows_draw(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata, DRWView DRW_view_set_active(view); - DRW_uniformbuffer_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */ + GPU_uniformbuf_update(sldata->shadow_ubo, &linfo->shadow_data); /* Update all data at once */ if (any_visible) { sldata->common_data.ray_type = saved_ray_type; - DRW_uniformbuffer_update(sldata->common_ubo, &sldata->common_data); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); } } diff --git a/source/blender/draw/engines/eevee/eevee_subsurface.c b/source/blender/draw/engines/eevee/eevee_subsurface.c index 74fb7ac99b7..5daa6e7c622 100644 --- a/source/blender/draw/engines/eevee/eevee_subsurface.c +++ b/source/blender/draw/engines/eevee/eevee_subsurface.c @@ -212,7 +212,7 @@ void EEVEE_subsurface_add_pass(EEVEE_ViewLayerData *sldata, GPUTexture **depth_src = GPU_depth_blitting_workaround() ? &effects->sss_stencil : &dtxl->depth; struct GPUTexture *sss_tex_profile = NULL; - struct GPUUniformBuffer *sss_profile = GPU_material_sss_profile_get( + struct GPUUniformBuf *sss_profile = GPU_material_sss_profile_get( gpumat, stl->effects->sss_sample_count, &sss_tex_profile); if (!sss_profile) { diff --git a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c index 5976a9505e8..4f2cfe224c2 100644 --- a/source/blender/draw/engines/eevee/eevee_temporal_sampling.c +++ b/source/blender/draw/engines/eevee/eevee_temporal_sampling.c @@ -247,8 +247,11 @@ int EEVEE_temporal_sampling_init(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data effects->taa_total_sample = first_sample_only ? 1 : scene_eval->eevee.taa_samples; MAX2(effects->taa_total_sample, 0); - DRW_view_persmat_get(NULL, persmat, false); - view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN); + /* Motion blur steps could reset the sampling when camera is animated (see T79970). */ + if (!DRW_state_is_scene_render()) { + DRW_view_persmat_get(NULL, persmat, false); + view_is_valid = view_is_valid && compare_m4m4(persmat, effects->prev_drw_persmat, FLT_MIN); + } /* Prevent ghosting from probe data. */ view_is_valid = view_is_valid && (effects->prev_drw_support == DRW_state_draw_support()) && diff --git a/source/blender/draw/engines/eevee/eevee_volumes.c b/source/blender/draw/engines/eevee/eevee_volumes.c index f8c7a6e16db..9e7e545167a 100644 --- a/source/blender/draw/engines/eevee/eevee_volumes.c +++ b/source/blender/draw/engines/eevee/eevee_volumes.c @@ -372,6 +372,8 @@ void EEVEE_volumes_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) /* If no world or volume material is present just clear the buffer with this drawcall */ grp = DRW_shgroup_create(e_data.volumetric_clear_sh, psl->volumetric_world_ps); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); + DRW_shgroup_uniform_block(grp, "light_block", sldata->light_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call_procedural_triangles(grp, NULL, common_data->vol_tex_size[2]); @@ -656,6 +658,7 @@ void EEVEE_volumes_cache_finish(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_shgroup_uniform_texture_ref(grp, "inTransmittance", &txl->volume_transmit); DRW_shgroup_uniform_texture_ref(grp, "inSceneDepth", &e_data.depth_src); DRW_shgroup_uniform_block(grp, "common_block", sldata->common_ubo); + DRW_shgroup_uniform_block(grp, "probe_block", sldata->probe_ubo); DRW_shgroup_uniform_block(grp, "renderpass_block", sldata->renderpass_ubo.combined); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index 51152475a06..7faf426c4e0 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -30,7 +30,7 @@ #include "BLI_math_color.h" #include "BLI_memblock.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "IMB_imbuf_types.h" @@ -46,7 +46,7 @@ static GPENCIL_MaterialPool *gpencil_material_pool_add(GPENCIL_PrivateData *pd) matpool->next = NULL; matpool->used_count = 0; if (matpool->ubo == NULL) { - matpool->ubo = GPU_uniformbuffer_create(sizeof(matpool->mat_data), NULL, NULL); + matpool->ubo = GPU_uniformbuf_create(sizeof(matpool->mat_data)); } pd->last_material_pool = matpool; return matpool; @@ -301,7 +301,7 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, int mat_id, GPUTexture **r_tex_stroke, GPUTexture **r_tex_fill, - GPUUniformBuffer **r_ubo_mat) + GPUUniformBuf **r_ubo_mat) { GPENCIL_MaterialPool *matpool = first_pool; int pool_id = mat_id / GP_MATERIAL_BUFFER_LEN; @@ -331,7 +331,7 @@ GPENCIL_LightPool *gpencil_light_pool_add(GPENCIL_PrivateData *pd) /* Tag light list end. */ lightpool->light_data[0].color[0] = -1.0; if (lightpool->ubo == NULL) { - lightpool->ubo = GPU_uniformbuffer_create(sizeof(lightpool->light_data), NULL, NULL); + lightpool->ubo = GPU_uniformbuf_create(sizeof(lightpool->light_data)); } pd->last_light_pool = lightpool; return lightpool; diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 015e631dc14..368530fde05 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -42,7 +42,7 @@ #include "DNA_view3d_types.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "gpencil_engine.h" @@ -345,8 +345,8 @@ typedef struct gpIterPopulateData { GPENCIL_MaterialPool *matpool; DRWShadingGroup *grp; /* Last material UBO bound. Used to avoid uneeded buffer binding. */ - GPUUniformBuffer *ubo_mat; - GPUUniformBuffer *ubo_lights; + GPUUniformBuf *ubo_mat; + GPUUniformBuf *ubo_lights; /* Last texture bound. */ GPUTexture *tex_fill; GPUTexture *tex_stroke; @@ -487,9 +487,10 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl, MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(iter->ob, gps->mat_nr + 1); + const bool is_render = iter->pd->is_render; bool hide_material = (gp_style->flag & GP_MATERIAL_HIDE) != 0; bool show_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) != 0) || - ((gps->flag & GP_STROKE_NOFILL) != 0); + (!is_render && ((gps->flag & GP_STROKE_NOFILL) != 0)); bool show_fill = (gps->tot_triangles > 0) && ((gp_style->flag & GP_MATERIAL_FILL_SHOW) != 0) && (!iter->pd->simplify_fill) && ((gps->flag & GP_STROKE_NOFILL) == 0); @@ -501,7 +502,7 @@ static void gpencil_stroke_cache_populate(bGPDlayer *gpl, return; } - GPUUniformBuffer *ubo_mat; + GPUUniformBuf *ubo_mat; GPUTexture *tex_stroke, *tex_fill; gpencil_material_resources_get( iter->matpool, iter->mat_ofs + gps->mat_nr, &tex_stroke, &tex_fill, &ubo_mat); @@ -654,13 +655,13 @@ void GPENCIL_cache_finish(void *ved) BLI_memblock_iternew(pd->gp_material_pool, &iter); GPENCIL_MaterialPool *pool; while ((pool = (GPENCIL_MaterialPool *)BLI_memblock_iterstep(&iter))) { - GPU_uniformbuffer_update(pool->ubo, pool->mat_data); + GPU_uniformbuf_update(pool->ubo, pool->mat_data); } BLI_memblock_iternew(pd->gp_light_pool, &iter); GPENCIL_LightPool *lpool; while ((lpool = (GPENCIL_LightPool *)BLI_memblock_iterstep(&iter))) { - GPU_uniformbuffer_update(lpool->ubo, lpool->light_data); + GPU_uniformbuf_update(lpool->ubo, lpool->light_data); } /* Sort object by decreasing Z to avoid most of alpha ordering issues. */ diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index a406df530fc..852945b25c3 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -24,10 +24,16 @@ #include "DNA_gpencil_types.h" +#include "DRW_render.h" + #include "BLI_bitmap.h" #include "GPU_batch.h" +#ifdef __cplusplus +extern "C" { +#endif + extern DrawEngineType draw_engine_gpencil_type; struct GPENCIL_Data; @@ -108,7 +114,7 @@ typedef struct GPENCIL_MaterialPool { /* GPU representatin of materials. */ gpMaterial mat_data[GP_MATERIAL_BUFFER_LEN]; /* Matching ubo. */ - struct GPUUniformBuffer *ubo; + struct GPUUniformBuf *ubo; /* Texture per material. NULL means none. */ struct GPUTexture *tex_fill[GP_MATERIAL_BUFFER_LEN]; struct GPUTexture *tex_stroke[GP_MATERIAL_BUFFER_LEN]; @@ -120,7 +126,7 @@ typedef struct GPENCIL_LightPool { /* GPU representatin of materials. */ gpLight light_data[GPENCIL_LIGHT_BUFFER_LEN]; /* Matching ubo. */ - struct GPUUniformBuffer *ubo; + struct GPUUniformBuf *ubo; /* Number of light in the pool. */ int light_used; } GPENCIL_LightPool; @@ -384,7 +390,7 @@ void gpencil_material_resources_get(GPENCIL_MaterialPool *first_pool, int mat_id, struct GPUTexture **r_tex_stroke, struct GPUTexture **r_tex_fill, - struct GPUUniformBuffer **r_ubo_mat); + struct GPUUniformBuf **r_ubo_mat); void gpencil_light_ambient_add(GPENCIL_LightPool *lightpool, const float color[3]); void gpencil_light_pool_populate(GPENCIL_LightPool *matpool, Object *ob); @@ -397,7 +403,6 @@ void gpencil_vfx_cache_populate(GPENCIL_Data *vedata, Object *ob, GPENCIL_tObjec /* Shaders */ struct GPUShader *GPENCIL_shader_antialiasing(int stage); struct GPUShader *GPENCIL_shader_geometry_get(void); -struct GPUShader *GPENCIL_shader_composite_get(void); struct GPUShader *GPENCIL_shader_layer_blend_get(void); struct GPUShader *GPENCIL_shader_mask_invert_get(void); struct GPUShader *GPENCIL_shader_depth_merge_get(void); @@ -438,3 +443,6 @@ void GPENCIL_render_to_image(void *vedata, void gpencil_light_pool_free(void *storage); void gpencil_material_pool_free(void *storage); GPENCIL_ViewLayerData *GPENCIL_view_layer_data_ensure(void); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 7e93382796f..c9d29af91e6 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -22,6 +22,12 @@ #pragma once +#include "DRW_render.h" + +#ifdef __cplusplus +extern "C" { +#endif + #ifdef __APPLE__ # define USE_GEOM_SHADER_WORKAROUND 1 #else @@ -627,3 +633,7 @@ GPUShader *OVERLAY_shader_xray_fade(void); OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void); void OVERLAY_shader_free(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl index eb61edca6c7..122c99ca536 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_common_lib.glsl @@ -1,6 +1,5 @@ #define EPSILON 0.00001 -#define M_PI 3.14159265358979323846 #define CAVITY_BUFFER_RANGE 4.0 diff --git a/source/blender/draw/engines/workbench/workbench_data.c b/source/blender/draw/engines/workbench/workbench_data.c index 0d7f4ee660b..b6cfc019b8d 100644 --- a/source/blender/draw/engines/workbench/workbench_data.c +++ b/source/blender/draw/engines/workbench/workbench_data.c @@ -32,24 +32,24 @@ #include "UI_resources.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" /* -------------------------------------------------------------------- */ /** \name World Data * \{ */ -GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd) +GPUUniformBuf *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd) { - struct GPUUniformBuffer **ubo = BLI_memblock_alloc(wpd->material_ubo); + struct GPUUniformBuf **ubo = BLI_memblock_alloc(wpd->material_ubo); if (*ubo == NULL) { - *ubo = GPU_uniformbuffer_create(sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL, NULL, NULL); + *ubo = GPU_uniformbuf_create(sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL); } return *ubo; } static void workbench_ubo_free(void *elem) { - GPUUniformBuffer **ubo = elem; + GPUUniformBuf **ubo = elem; DRW_UBO_FREE_SAFE(*ubo); } @@ -78,7 +78,7 @@ static WORKBENCH_ViewLayerData *workbench_view_layer_data_ensure_ex(struct ViewL size_t matbuf_size = sizeof(WORKBENCH_UBO_Material) * MAX_MATERIAL; (*vldata)->material_ubo_data = BLI_memblock_create_ex(matbuf_size, matbuf_size * 2); (*vldata)->material_ubo = BLI_memblock_create_ex(sizeof(void *), sizeof(void *) * 8); - (*vldata)->world_ubo = DRW_uniformbuffer_create(sizeof(WORKBENCH_UBO_World), NULL); + (*vldata)->world_ubo = GPU_uniformbuf_create_ex(sizeof(WORKBENCH_UBO_World), NULL, "wb_World"); } return *vldata; @@ -275,7 +275,7 @@ void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd) workbench_shadow_data_update(wpd, &wd); workbench_cavity_data_update(wpd, &wd); - DRW_uniformbuffer_update(wpd->world_ubo, &wd); + GPU_uniformbuf_update(wpd->world_ubo, &wd); } void workbench_update_material_ubos(WORKBENCH_PrivateData *UNUSED(wpd)) @@ -288,9 +288,9 @@ void workbench_update_material_ubos(WORKBENCH_PrivateData *UNUSED(wpd)) BLI_memblock_iternew(vldata->material_ubo_data, &iter_data); WORKBENCH_UBO_Material *matchunk; while ((matchunk = BLI_memblock_iterstep(&iter_data))) { - GPUUniformBuffer **ubo = BLI_memblock_iterstep(&iter); + GPUUniformBuf **ubo = BLI_memblock_iterstep(&iter); BLI_assert(*ubo != NULL); - GPU_uniformbuffer_update(*ubo, matchunk); + GPU_uniformbuf_update(*ubo, matchunk); } BLI_memblock_clear(vldata->material_ubo, workbench_ubo_free); diff --git a/source/blender/draw/engines/workbench/workbench_effect_cavity.c b/source/blender/draw/engines/workbench/workbench_effect_cavity.c index 4a8db65c02e..c9ac6660445 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_cavity.c +++ b/source/blender/draw/engines/workbench/workbench_effect_cavity.c @@ -139,8 +139,8 @@ void workbench_cavity_samples_ubo_ensure(WORKBENCH_PrivateData *wpd) float *samples = create_disk_samples(cavity_sample_count_single_iteration, max_iter_count); wpd->vldata->cavity_jitter_tx = create_jitter_texture(cavity_sample_count); /* NOTE: Uniform buffer needs to always be filled to be valid. */ - wpd->vldata->cavity_sample_ubo = DRW_uniformbuffer_create( - sizeof(float[4]) * CAVITY_MAX_SAMPLES, samples); + wpd->vldata->cavity_sample_ubo = GPU_uniformbuf_create_ex( + sizeof(float[4]) * CAVITY_MAX_SAMPLES, samples, "wb_CavitySamples"); wpd->vldata->cavity_sample_count = cavity_sample_count; MEM_freeN(samples); } diff --git a/source/blender/draw/engines/workbench/workbench_effect_dof.c b/source/blender/draw/engines/workbench/workbench_effect_dof.c index 32f6a3392b5..abbca0988d4 100644 --- a/source/blender/draw/engines/workbench/workbench_effect_dof.c +++ b/source/blender/draw/engines/workbench/workbench_effect_dof.c @@ -74,7 +74,7 @@ static void square_to_circle(float x, float y, float *r, float *T) #define KERNEL_RAD (3) #define SAMP_LEN SQUARE_UNSAFE(KERNEL_RAD * 2 + 1) -static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo, +static void workbench_dof_setup_samples(struct GPUUniformBuf **ubo, float **data, float bokeh_sides, float bokeh_rotation, @@ -84,7 +84,7 @@ static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo, *data = MEM_callocN(sizeof(float[4]) * SAMP_LEN, "workbench dof samples"); } if (*ubo == NULL) { - *ubo = DRW_uniformbuffer_create(sizeof(float[4]) * SAMP_LEN, NULL); + *ubo = GPU_uniformbuf_create(sizeof(float[4]) * SAMP_LEN); } float *samp = *data; @@ -120,7 +120,7 @@ static void workbench_dof_setup_samples(struct GPUUniformBuffer **ubo, } } - DRW_uniformbuffer_update(*ubo, *data); + GPU_uniformbuf_update(*ubo, *data); } void workbench_dof_engine_init(WORKBENCH_Data *vedata) diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index ca80b6a9002..80a8f310191 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -114,7 +114,7 @@ static void workbench_cache_sculpt_populate(WORKBENCH_PrivateData *wpd, eV3DShadingColorType color_type) { const bool use_single_drawcall = !ELEM(color_type, V3D_SHADING_MATERIAL_COLOR); - BLI_assert(wpd->shading.color_type != V3D_SHADING_TEXTURE_COLOR); + BLI_assert(color_type != V3D_SHADING_TEXTURE_COLOR); if (use_single_drawcall) { DRWShadingGroup *grp = workbench_material_setup(wpd, ob, 0, color_type, NULL); @@ -309,6 +309,11 @@ static eV3DShadingColorType workbench_color_type_get(WORKBENCH_PrivateData *wpd, } } + if (is_sculpt_pbvh && color_type == V3D_SHADING_TEXTURE_COLOR) { + /* Force use of material color for sculpt. */ + color_type = V3D_SHADING_MATERIAL_COLOR; + } + if (r_draw_shadow) { *r_draw_shadow = (ob->dtx & OB_DRAW_NO_SHADOW_CAST) == 0 && SHADOW_ENABLED(wpd); /* Currently unsupported in sculpt mode. We could revert to the slow diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 538083b4beb..6aa794bda51 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -33,7 +33,7 @@ #include "DNA_mesh_types.h" #include "DNA_node_types.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "ED_uvedit.h" diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 4a6dadc32fd..8983826f16f 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -33,6 +33,10 @@ #include "workbench_engine.h" +#ifdef __cplusplus +extern "C" { +#endif + extern struct DrawEngineType draw_engine_workbench; #define WORKBENCH_ENGINE "BLENDER_WORKBENCH" @@ -238,7 +242,7 @@ typedef struct WORKBENCH_PrivateData { /** Copy of context mode for faster access. */ eContextObjectMode ctx_mode; /** Shorthand for wpd->vldata->world_ubo. */ - struct GPUUniformBuffer *world_ubo; + struct GPUUniformBuf *world_ubo; /** Background color to clear the color buffer with. */ float background_color[4]; @@ -309,7 +313,7 @@ typedef struct WORKBENCH_PrivateData { struct BLI_memblock *material_ubo_data; /** Current material chunk being filled by workbench_material_setup_ex(). */ WORKBENCH_UBO_Material *material_ubo_data_curr; - struct GPUUniformBuffer *material_ubo_curr; + struct GPUUniformBuf *material_ubo_curr; /** Copy of txl->dummy_image_tx for faster access. */ struct GPUTexture *dummy_image_tx; /** Total number of used material chunk. */ @@ -359,11 +363,11 @@ typedef struct WORKBENCH_ObjectData { typedef struct WORKBENCH_ViewLayerData { /** Depth of field sample location array.*/ - struct GPUUniformBuffer *dof_sample_ubo; + struct GPUUniformBuf *dof_sample_ubo; /** All constant data used for a render loop.*/ - struct GPUUniformBuffer *world_ubo; + struct GPUUniformBuf *world_ubo; /** Cavity sample location array.*/ - struct GPUUniformBuffer *cavity_sample_ubo; + struct GPUUniformBuf *cavity_sample_ubo; /** Blue noise texture used to randomize the sampling of some effects.*/ struct GPUTexture *cavity_jitter_tx; /** Materials ubos allocated in a memblock for easy bookeeping. */ @@ -490,7 +494,7 @@ DRWShadingGroup *workbench_image_setup_ex(WORKBENCH_PrivateData *wpd, void workbench_private_data_init(WORKBENCH_PrivateData *wpd); void workbench_update_world_ubo(WORKBENCH_PrivateData *wpd); void workbench_update_material_ubos(WORKBENCH_PrivateData *wpd); -struct GPUUniformBuffer *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd); +struct GPUUniformBuf *workbench_material_ubo_alloc(WORKBENCH_PrivateData *wpd); /* workbench_volume.c */ void workbench_volume_engine_init(WORKBENCH_Data *vedata); @@ -519,3 +523,6 @@ void workbench_render(void *ved, void workbench_render_update_passes(struct RenderEngine *engine, struct Scene *scene, struct ViewLayer *view_layer); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 63625fae185..697f4f77601 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -48,6 +48,7 @@ #include "GPU_primitive.h" #include "GPU_shader.h" #include "GPU_texture.h" +#include "GPU_uniform_buffer.h" #include "draw_cache.h" #include "draw_common.h" @@ -67,7 +68,7 @@ struct GPUFrameBuffer; struct GPUMaterial; struct GPUShader; struct GPUTexture; -struct GPUUniformBuffer; +struct GPUUniformBuf; struct Object; struct ParticleSystem; struct RenderEngineType; @@ -184,14 +185,10 @@ void DRW_texture_free(struct GPUTexture *tex); } \ } while (0) -/* UBOs */ -struct GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data); -void DRW_uniformbuffer_update(struct GPUUniformBuffer *ubo, const void *data); -void DRW_uniformbuffer_free(struct GPUUniformBuffer *ubo); #define DRW_UBO_FREE_SAFE(ubo) \ do { \ if (ubo != NULL) { \ - DRW_uniformbuffer_free(ubo); \ + GPU_uniformbuf_free(ubo); \ ubo = NULL; \ } \ } while (0) @@ -516,10 +513,10 @@ void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, struct GPUTexture **tex); void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, - const struct GPUUniformBuffer *ubo); + const struct GPUUniformBuf *ubo); void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, - struct GPUUniformBuffer **ubo); + struct GPUUniformBuf **ubo); void DRW_shgroup_uniform_float(DRWShadingGroup *shgroup, const char *name, const float *value, diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index 2653b3127ae..5f77dac98be 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -260,6 +260,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, Mesh *me, const bool is_editmode, const bool is_paint_mode, + const bool is_mode_active, const float obmat[4][4], const bool do_final, const bool do_uvedit, diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.c b/source/blender/draw/intern/draw_cache_extract_mesh.c index 934b47d583e..528a6eec69e 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.c +++ b/source/blender/draw/intern/draw_cache_extract_mesh.c @@ -99,8 +99,6 @@ typedef struct MeshRenderData { float obmat[4][4]; const ToolSettings *toolsettings; - /* HACK not supposed to be there but it's needed. */ - struct MeshBatchCache *cache; /** Edit Mesh */ BMEditMesh *edit_bmesh; BMesh *bm; @@ -313,9 +311,14 @@ static void mesh_render_data_update_normals(MeshRenderData *mr, } } +/** + * \param is_mode_active: When true, use the modifiers from the edit-data, + * otherwise don't use modifiers as they are not from this object. + */ static MeshRenderData *mesh_render_data_create(Mesh *me, const bool is_editmode, const bool is_paint_mode, + const bool is_mode_active, const float obmat[4][4], const bool do_final, const bool do_uvedit, @@ -335,7 +338,7 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, mr->bm = me->edit_mesh->bm; mr->edit_bmesh = me->edit_mesh; mr->me = (do_final) ? me->edit_mesh->mesh_eval_final : me->edit_mesh->mesh_eval_cage; - mr->edit_data = mr->me->runtime.edit_data; + mr->edit_data = is_mode_active ? mr->me->runtime.edit_data : NULL; if (mr->edit_data) { EditMeshData *emd = mr->edit_data; @@ -350,8 +353,9 @@ static MeshRenderData *mesh_render_data_create(Mesh *me, mr->bm_poly_centers = mr->edit_data->polyCos; } - bool has_mdata = (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); - bool use_mapped = has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original; + bool has_mdata = is_mode_active && (mr->me->runtime.wrapper_type == ME_WRAPPER_TYPE_MDATA); + bool use_mapped = is_mode_active && + (has_mdata && !do_uvedit && mr->me && !mr->me->runtime.is_original); int bm_ensure_types = BM_VERT | BM_EDGE | BM_LOOP | BM_FACE; @@ -751,8 +755,13 @@ typedef void(ExtractLVertMeshFn)(const MeshRenderData *mr, /** \name Mesh Elements Extract Struct * \{ */ -typedef void *(ExtractInitFn)(const MeshRenderData *mr, void *buffer); -typedef void(ExtractFinishFn)(const MeshRenderData *mr, void *buffer, void *data); +typedef void *(ExtractInitFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer); +typedef void(ExtractFinishFn)(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buffer, + void *data); typedef struct MeshExtract { /** Executed on main thread and return user data for iteration functions. */ @@ -796,7 +805,9 @@ typedef struct MeshExtract_Tri_Data { int *tri_mat_end; } MeshExtract_Tri_Data; -static void *extract_tris_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_Tri_Data *data = MEM_callocN(sizeof(*data), __func__); @@ -882,14 +893,17 @@ static void extract_tris_iter_looptri_mesh(const MeshRenderData *mr, EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } -static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data) +static void extract_tris_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *ibo, + void *_data) { MeshExtract_Tri_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); /* HACK: Create ibo sub-ranges and assign them to each #GPUBatch. */ /* The `surface_per_mat` tests are there when object shading type is set to Wire or Bounds. In * these cases there isn't a surface per material. */ - if (mr->use_final_mesh && mr->cache->surface_per_mat && mr->cache->surface_per_mat[0]) { + if (mr->use_final_mesh && cache->surface_per_mat && cache->surface_per_mat[0]) { for (int i = 0; i < mr->mat_len; i++) { /* Multiply by 3 because these are triangle indices. */ const int mat_start = data->tri_mat_start[i]; @@ -898,7 +912,7 @@ static void extract_tris_finish(const MeshRenderData *mr, void *ibo, void *_data const int len = (mat_end - mat_start) * 3; GPUIndexBuf *sub_ibo = GPU_indexbuf_create_subrange(ibo, start, len); /* WARNING: We modify the #GPUBatch here! */ - GPU_batch_elembuf_set(mr->cache->surface_per_mat[i], sub_ibo, true); + GPU_batch_elembuf_set(cache->surface_per_mat[i], sub_ibo, true); } } MEM_freeN(data->tri_mat_start); @@ -921,7 +935,9 @@ static const MeshExtract extract_tris = { /** \name Extract Edges Indices * \{ */ -static void *extract_lines_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); /* Put loose edges at the end. */ @@ -1039,7 +1055,10 @@ static void extract_lines_iter_ledge_mesh(const MeshRenderData *mr, EXTRACT_LEDGE_FOREACH_MESH_END; } -static void extract_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb) +static void extract_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *elb) { GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); @@ -1061,21 +1080,24 @@ static const MeshExtract extract_lines = { /** \name Extract Loose Edges Sub Buffer * \{ */ -static void extract_lines_loose_subbuffer(const MeshRenderData *mr) +static void extract_lines_loose_subbuffer(const MeshRenderData *mr, struct MeshBatchCache *cache) { - BLI_assert(mr->cache->final.ibo.lines); + BLI_assert(cache->final.ibo.lines); /* Multiply by 2 because these are edges indices. */ const int start = mr->edge_len * 2; const int len = mr->edge_loose_len * 2; GPU_indexbuf_create_subrange_in_place( - mr->cache->final.ibo.lines_loose, mr->cache->final.ibo.lines, start, len); - mr->cache->no_loose_wire = (len == 0); + cache->final.ibo.lines_loose, cache->final.ibo.lines, start, len); + cache->no_loose_wire = (len == 0); } -static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, void *ibo, void *elb) +static void extract_lines_with_lines_loose_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *ibo, + void *elb) { GPU_indexbuf_build_in_place(elb, ibo); - extract_lines_loose_subbuffer(mr); + extract_lines_loose_subbuffer(mr, cache); MEM_freeN(elb); } @@ -1096,7 +1118,9 @@ static const MeshExtract extract_lines_with_lines_loose = { /** \name Extract Point Indices * \{ */ -static void *extract_points_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->vert_len, mr->loop_len + mr->loop_loose_len); @@ -1200,7 +1224,10 @@ static void extract_points_iter_lvert_mesh(const MeshRenderData *mr, EXTRACT_LVERT_FOREACH_MESH_END; } -static void extract_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb) +static void extract_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *elb) { GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); @@ -1225,7 +1252,9 @@ static const MeshExtract extract_points = { /** \name Extract Facedots Indices * \{ */ -static void *extract_fdots_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { GPUIndexBufBuilder *elb = MEM_mallocN(sizeof(*elb), __func__); GPU_indexbuf_init(elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); @@ -1280,7 +1309,10 @@ static void extract_fdots_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *elb) +static void extract_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *elb) { GPU_indexbuf_build_in_place(elb, ibo); MEM_freeN(elb); @@ -1307,7 +1339,9 @@ typedef struct MeshExtract_LinePaintMask_Data { BLI_bitmap select_map[0]; } MeshExtract_LinePaintMask_Data; -static void *extract_lines_paint_mask_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_lines_paint_mask_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { size_t bitmap_size = BLI_BITMAP_SIZE(mr->edge_len); MeshExtract_LinePaintMask_Data *data = MEM_callocN(sizeof(*data) + bitmap_size, __func__); @@ -1354,6 +1388,7 @@ static void extract_lines_paint_mask_iter_poly_mesh(const MeshRenderData *mr, EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } static void extract_lines_paint_mask_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *ibo, void *_data) { @@ -1387,7 +1422,9 @@ typedef struct MeshExtract_LineAdjacency_Data { uint vert_to_loop[0]; } MeshExtract_LineAdjacency_Data; -static void *extract_lines_adjacency_init(const MeshRenderData *mr, void *UNUSED(buf)) +static void *extract_lines_adjacency_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf)) { /* Similar to poly_to_tri_count(). * There is always (loop + triangle - 1) edges inside a polygon. @@ -1483,7 +1520,10 @@ static void extract_lines_adjacency_iter_looptri_mesh(const MeshRenderData *mr, EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } -static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo, void *_data) +static void extract_lines_adjacency_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *cache, + void *ibo, + void *_data) { MeshExtract_LineAdjacency_Data *data = _data; /* Create edges for remaining non manifold edges. */ @@ -1506,7 +1546,7 @@ static void extract_lines_adjacency_finish(const MeshRenderData *mr, void *ibo, BLI_edgehashIterator_free(ehi); BLI_edgehash_free(data->eh, NULL); - mr->cache->is_manifold = data->is_manifold; + cache->is_manifold = data->is_manifold; GPU_indexbuf_build_in_place(&data->elb, ibo); MEM_freeN(data); @@ -1534,7 +1574,9 @@ typedef struct MeshExtract_EditUvElem_Data { bool sync_selection; } MeshExtract_EditUvElem_Data; -static void *extract_edituv_tris_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_edituv_tris_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_TRIS, mr->tri_len, mr->loop_len); @@ -1583,7 +1625,10 @@ static void extract_edituv_tris_iter_looptri_mesh(const MeshRenderData *mr, EXTRACT_TRIS_LOOPTRI_FOREACH_MESH_END; } -static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +static void extract_edituv_tris_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *data) { MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); @@ -1605,7 +1650,9 @@ static const MeshExtract extract_edituv_tris = { /** \name Extract Edit UV Line Indices around faces * \{ */ -static void *extract_edituv_lines_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_edituv_lines_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_LINES, mr->loop_len, mr->loop_len); @@ -1655,7 +1702,10 @@ static void extract_edituv_lines_iter_poly_mesh(const MeshRenderData *mr, EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +static void extract_edituv_lines_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *data) { MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); @@ -1677,7 +1727,9 @@ static const MeshExtract extract_edituv_lines = { /** \name Extract Edit UV Points Indices * \{ */ -static void *extract_edituv_points_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_edituv_points_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->loop_len, mr->loop_len); @@ -1724,7 +1776,10 @@ static void extract_edituv_points_iter_poly_mesh(const MeshRenderData *mr, EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *data) +static void extract_edituv_points_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *data) { MeshExtract_EditUvElem_Data *extract_data = data; GPU_indexbuf_build_in_place(&extract_data->elb, ibo); @@ -1746,7 +1801,9 @@ static const MeshExtract extract_edituv_points = { /** \name Extract Edit UV Facedots Indices * \{ */ -static void *extract_edituv_fdots_init(const MeshRenderData *mr, void *UNUSED(ibo)) +static void *extract_edituv_fdots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(ibo)) { MeshExtract_EditUvElem_Data *data = MEM_callocN(sizeof(*data), __func__); GPU_indexbuf_init(&data->elb, GPU_PRIM_POINTS, mr->poly_len, mr->poly_len); @@ -1813,7 +1870,10 @@ static void extract_edituv_fdots_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), void *ibo, void *_data) +static void extract_edituv_fdots_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *ibo, + void *_data) { MeshExtract_EditUvElem_Data *data = _data; GPU_indexbuf_build_in_place(&data->elb, ibo); @@ -1845,7 +1905,9 @@ typedef struct MeshExtract_PosNor_Data { GPUPackedNormal packed_nor[]; } MeshExtract_PosNor_Data; -static void *extract_pos_nor_init(const MeshRenderData *mr, void *buf) +static void *extract_pos_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -1991,7 +2053,10 @@ static void extract_pos_nor_iter_lvert_mesh(const MeshRenderData *mr, EXTRACT_LVERT_FOREACH_MESH_END; } -static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(vbo), void *data) +static void extract_pos_nor_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(vbo), + void *data) { MEM_freeN(data); } @@ -2018,7 +2083,9 @@ typedef struct gpuHQNor { short x, y, z, w; } gpuHQNor; -static void *extract_lnor_hq_init(const MeshRenderData *mr, void *buf) +static void *extract_lnor_hq_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2104,7 +2171,9 @@ static const MeshExtract extract_lnor_hq = { /** \name Extract Loop Normal * \{ */ -static void *extract_lnor_init(const MeshRenderData *mr, void *buf) +static void *extract_lnor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2195,16 +2264,15 @@ static const MeshExtract extract_lnor = { /** \name Extract UV layers * \{ */ -static void *extract_uv_init(const MeshRenderData *mr, void *buf) +static void *extract_uv_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; - uint32_t uv_layers = mr->cache->cd_used.uv; - + uint32_t uv_layers = cache->cd_used.uv; /* HACK to fix T68857 */ - if (mr->extract_type == MR_EXTRACT_BMESH && mr->cache->cd_used.edit_uv == 1) { + if (mr->extract_type == MR_EXTRACT_BMESH && cache->cd_used.edit_uv == 1) { int layer = CustomData_get_active_layer(cd_ldata, CD_MLOOPUV); if (layer != -1) { uv_layers |= (1 << layer); @@ -2292,7 +2360,10 @@ static const MeshExtract extract_uv = { /** \name Extract Tangent layers * \{ */ -static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool do_hq) +static void extract_tan_ex(const MeshRenderData *mr, + struct MeshBatchCache *cache, + GPUVertBuf *vbo, + const bool do_hq) { GPUVertCompType comp_type = do_hq ? GPU_COMP_I16 : GPU_COMP_I10; GPUVertFetchMode fetch_mode = GPU_FETCH_INT_TO_FLOAT_UNIT; @@ -2302,10 +2373,10 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - uint32_t tan_layers = mr->cache->cd_used.tan; + uint32_t tan_layers = cache->cd_used.tan; float(*orco)[3] = CustomData_get_layer(cd_vdata, CD_ORCO); bool orco_allocated = false; - const bool use_orco_tan = mr->cache->cd_used.tan_orco != 0; + const bool use_orco_tan = cache->cd_used.tan_orco != 0; int tan_len = 0; char tangent_names[MAX_MTFACE][MAX_CUSTOMDATA_LAYER_NAME]; @@ -2461,9 +2532,9 @@ static void extract_tan_ex(const MeshRenderData *mr, GPUVertBuf *vbo, const bool CustomData_free(&loop_data, mr->loop_len); } -static void *extract_tan_init(const MeshRenderData *mr, void *buf) +static void *extract_tan_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, buf, false); + extract_tan_ex(mr, cache, buf, false); return NULL; } @@ -2479,9 +2550,9 @@ static const MeshExtract extract_tan = { /** \name Extract HQ Tangent layers * \{ */ -static void *extract_tan_hq_init(const MeshRenderData *mr, void *buf) +static void *extract_tan_hq_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { - extract_tan_ex(mr, buf, true); + extract_tan_ex(mr, cache, buf, true); return NULL; } @@ -2497,15 +2568,15 @@ static const MeshExtract extract_tan_hq = { /** \name Extract VCol * \{ */ -static void *extract_vcol_init(const MeshRenderData *mr, void *buf) +static void *extract_vcol_init(const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf) { GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; - uint32_t vcol_layers = mr->cache->cd_used.vcol; - uint32_t svcol_layers = mr->cache->cd_used.sculpt_vcol; + uint32_t vcol_layers = cache->cd_used.vcol; + uint32_t svcol_layers = cache->cd_used.sculpt_vcol; for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { @@ -2652,7 +2723,9 @@ typedef struct MeshExtract_Orco_Data { float (*orco)[3]; } MeshExtract_Orco_Data; -static void *extract_orco_init(const MeshRenderData *mr, void *buf) +static void *extract_orco_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2705,7 +2778,10 @@ static void extract_orco_iter_poly_mesh(const MeshRenderData *mr, EXTRACT_POLY_AND_LOOP_FOREACH_MESH_END; } -static void extract_orco_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data) +static void extract_orco_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) { MEM_freeN(data); } @@ -2749,7 +2825,9 @@ static float loop_edge_factor_get(const float f_no[3], return d; } -static void *extract_edge_fac_init(const MeshRenderData *mr, void *buf) +static void *extract_edge_fac_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2874,7 +2952,10 @@ static void extract_edge_fac_iter_ledge_mesh(const MeshRenderData *mr, EXTRACT_LEDGE_FOREACH_MESH_END; } -static void extract_edge_fac_finish(const MeshRenderData *mr, void *buf, void *_data) +static void extract_edge_fac_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *_data) { MeshExtract_EdgeFac_Data *data = _data; @@ -2981,7 +3062,9 @@ static float evaluate_vertex_weight(const MDeformVert *dvert, const DRW_MeshWeig return input; } -static void *extract_weights_init(const MeshRenderData *mr, void *buf) +static void *extract_weights_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -2993,7 +3076,7 @@ static void *extract_weights_init(const MeshRenderData *mr, void *buf) MeshExtract_Weight_Data *data = MEM_callocN(sizeof(*data), __func__); data->vbo_data = (float *)vbo->data; - data->wstate = &mr->cache->weight_state; + data->wstate = &cache->weight_state; if (data->wstate->defgroup_active == -1) { /* Nothing to show. */ @@ -3056,7 +3139,10 @@ static void extract_weights_iter_poly_mesh(const MeshRenderData *mr, } } -static void extract_weights_finish(const MeshRenderData *UNUSED(mr), void *UNUSED(buf), void *data) +static void extract_weights_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), + void *UNUSED(buf), + void *data) { MEM_freeN(data); } @@ -3214,7 +3300,9 @@ static void mesh_render_data_vert_flag(const MeshRenderData *mr, BMVert *eve, Ed } } -static void *extract_edit_data_init(const MeshRenderData *mr, void *buf) +static void *extract_edit_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -3365,7 +3453,9 @@ typedef struct MeshExtract_EditUVData_Data { int cd_ofs; } MeshExtract_EditUVData_Data; -static void *extract_edituv_data_init(const MeshRenderData *mr, void *buf) +static void *extract_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -3442,6 +3532,7 @@ static void extract_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } static void extract_edituv_data_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), void *data) { @@ -3463,7 +3554,9 @@ static const MeshExtract extract_edituv_data = { /** \name Extract Edit UV area stretch * \{ */ -static void *extract_stretch_area_init(const MeshRenderData *mr, void *buf) +static void *extract_stretch_area_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -3492,7 +3585,10 @@ BLI_INLINE float area_ratio_to_stretch(float ratio, float tot_ratio, float inv_t return (ratio > 1.0f) ? (1.0f / ratio) : ratio; } -static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +static void mesh_stretch_area_finish(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(data)) { float tot_area = 0.0f, tot_uv_area = 0.0f; float *area_ratio = MEM_mallocN(sizeof(float) * mr->poly_len, __func__); @@ -3528,8 +3624,8 @@ static void mesh_stretch_area_finish(const MeshRenderData *mr, void *buf, void * BLI_assert(0); } - mr->cache->tot_area = tot_area; - mr->cache->tot_uv_area = tot_uv_area; + cache->tot_area = tot_area; + cache->tot_uv_area = tot_uv_area; /* Convert in place to avoid an extra allocation */ uint16_t *poly_stretch = (uint16_t *)area_ratio; @@ -3634,7 +3730,9 @@ static void edituv_get_stretch_angle(float auv[2][2], #endif } -static void *extract_stretch_angle_init(const MeshRenderData *mr, void *buf) +static void *extract_stretch_angle_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -3755,6 +3853,7 @@ static void extract_stretch_angle_iter_poly_mesh(const MeshRenderData *mr, } static void extract_stretch_angle_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), void *data) { @@ -3776,7 +3875,9 @@ static const MeshExtract extract_stretch_angle = { /** \name Extract Edit Mesh Analysis Colors * \{ */ -static void *extract_mesh_analysis_init(const MeshRenderData *mr, void *buf) +static void *extract_mesh_analysis_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4337,7 +4438,10 @@ static void statvis_calc_sharp(const MeshRenderData *mr, float *r_sharp) MEM_freeN(vert_angles); } -static void extract_mesh_analysis_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +static void extract_mesh_analysis_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) { BLI_assert(mr->edit_bmesh); @@ -4378,7 +4482,9 @@ static const MeshExtract extract_mesh_analysis = { /** \name Extract Facedots positions * \{ */ -static void *extract_fdots_pos_init(const MeshRenderData *mr, void *buf) +static void *extract_fdots_pos_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4464,7 +4570,9 @@ static const MeshExtract extract_fdots_pos = { #define NOR_AND_FLAG_ACTIVE -1 #define NOR_AND_FLAG_HIDDEN -2 -static void *extract_fdots_nor_init(const MeshRenderData *mr, void *buf) +static void *extract_fdots_nor_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4477,7 +4585,10 @@ static void *extract_fdots_nor_init(const MeshRenderData *mr, void *buf) return NULL; } -static void extract_fdots_nor_finish(const MeshRenderData *mr, void *buf, void *UNUSED(data)) +static void extract_fdots_nor_finish(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf, + void *UNUSED(data)) { static float invalid_normal[3] = {0.0f, 0.0f, 0.0f}; GPUVertBuf *vbo = buf; @@ -4542,7 +4653,9 @@ typedef struct MeshExtract_FdotUV_Data { int cd_ofs; } MeshExtract_FdotUV_Data; -static void *extract_fdots_uv_init(const MeshRenderData *mr, void *buf) +static void *extract_fdots_uv_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4611,6 +4724,7 @@ static void extract_fdots_uv_iter_poly_mesh(const MeshRenderData *mr, } static void extract_fdots_uv_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), void *data) { @@ -4636,7 +4750,9 @@ typedef struct MeshExtract_EditUVFdotData_Data { int cd_ofs; } MeshExtract_EditUVFdotData_Data; -static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, void *buf) +static void *extract_fdots_edituv_data_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4684,6 +4800,7 @@ static void extract_fdots_edituv_data_iter_poly_mesh(const MeshRenderData *mr, } static void extract_fdots_edituv_data_finish(const MeshRenderData *UNUSED(mr), + struct MeshBatchCache *UNUSED(cache), void *UNUSED(buf), void *data) { @@ -4709,7 +4826,9 @@ typedef struct SkinRootData { float local_pos[3]; } SkinRootData; -static void *extract_skin_roots_init(const MeshRenderData *mr, void *buf) +static void *extract_skin_roots_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { /* Exclusively for edit mode. */ BLI_assert(mr->bm); @@ -4758,7 +4877,9 @@ static const MeshExtract extract_skin_roots = { /** \name Extract Selection Index * \{ */ -static void *extract_select_idx_init(const MeshRenderData *mr, void *buf) +static void *extract_select_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -4950,7 +5071,9 @@ static const MeshExtract extract_vert_idx = { .use_threading = true, }; -static void *extract_select_fdot_idx_init(const MeshRenderData *mr, void *buf) +static void *extract_select_fdot_idx_init(const MeshRenderData *mr, + struct MeshBatchCache *UNUSED(cache), + void *buf) { static GPUVertFormat format = {0}; if (format.attr_len == 0) { @@ -5019,6 +5142,7 @@ typedef enum ExtractTaskDataType { typedef struct ExtractTaskData { void *next, *prev; const MeshRenderData *mr; + struct MeshBatchCache *cache; const MeshExtract *extract; ExtractTaskDataType tasktype; eMRIterType iter_type; @@ -5030,6 +5154,7 @@ typedef struct ExtractTaskData { } ExtractTaskData; static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderData *mr, + struct MeshBatchCache *cache, const MeshExtract *extract, void *buf, int32_t *task_counter) @@ -5039,6 +5164,7 @@ static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderDa taskdata->prev = NULL; taskdata->tasktype = EXTRACT_MESH_EXTRACT; taskdata->mr = mr; + taskdata->cache = cache; taskdata->extract = extract; taskdata->buf = buf; @@ -5056,11 +5182,13 @@ static ExtractTaskData *extract_task_data_create_mesh_extract(const MeshRenderDa return taskdata; } -static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr) +static ExtractTaskData *extract_task_data_create_lines_loose(const MeshRenderData *mr, + struct MeshBatchCache *cache) { ExtractTaskData *taskdata = MEM_callocN(sizeof(*taskdata), __func__); taskdata->tasktype = EXTRACT_LINES_LOOSE; taskdata->mr = mr; + taskdata->cache = cache; return taskdata; } @@ -5152,7 +5280,7 @@ BLI_INLINE void mesh_extract_iter(const MeshRenderData *mr, static void extract_init(ExtractTaskData *data) { if (data->tasktype == EXTRACT_MESH_EXTRACT) { - data->user_data->user_data = data->extract->init(data->mr, data->buf); + data->user_data->user_data = data->extract->init(data->mr, data->cache, data->buf); } } @@ -5170,11 +5298,11 @@ static void extract_run(void *__restrict taskdata) /* If this is the last task, we do the finish function. */ int remainin_tasks = atomic_sub_and_fetch_int32(data->task_counter, 1); if (remainin_tasks == 0 && data->extract->finish != NULL) { - data->extract->finish(data->mr, data->buf, data->user_data->user_data); + data->extract->finish(data->mr, data->cache, data->buf, data->user_data->user_data); } } else if (data->tasktype == EXTRACT_LINES_LOOSE) { - extract_lines_loose_subbuffer(data->mr); + extract_lines_loose_subbuffer(data->mr, data->cache); } } @@ -5341,6 +5469,7 @@ static void extract_task_create(struct TaskGraph *task_graph, ListBase *user_data_init_task_datas, const Scene *scene, const MeshRenderData *mr, + MeshBatchCache *cache, const MeshExtract *extract, void *buf, int32_t *task_counter) @@ -5356,7 +5485,7 @@ static void extract_task_create(struct TaskGraph *task_graph, /* Divide extraction of the VBO/IBO into sensible chunks of works. */ ExtractTaskData *taskdata = extract_task_data_create_mesh_extract( - mr, extract, buf, task_counter); + mr, cache, extract, buf, task_counter); /* Simple heuristic. */ const int chunk_size = 8192; @@ -5411,6 +5540,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, const bool is_editmode, const bool is_paint_mode, + const bool is_mode_active, const float obmat[4][4], const bool do_final, const bool do_uvedit, @@ -5510,6 +5640,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, MeshRenderData *mr = mesh_render_data_create(me, is_editmode, is_paint_mode, + is_mode_active, obmat, do_final, do_uvedit, @@ -5517,7 +5648,6 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, ts, iter_flag, data_flag); - mr->cache = cache; /* HACK */ mr->use_hide = use_hide; mr->use_subsurf_fdots = use_subsurf_fdots; mr->use_final_mesh = do_final; @@ -5549,6 +5679,7 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, &user_data_init_task_data->task_datas, \ scene, \ mr, \ + cache, \ &extract_##name, \ mbc.buf.name, \ &task_counters[counter_used++]); \ @@ -5592,13 +5723,14 @@ void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, &user_data_init_task_data->task_datas, scene, mr, + cache, lines_extractor, mbc.ibo.lines, &task_counters[counter_used++]); } else { if (do_lines_loose_subbuffer) { - ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr); + ExtractTaskData *taskdata = extract_task_data_create_lines_loose(mr, cache); BLI_addtail(&single_threaded_task_data->task_datas, taskdata); } } diff --git a/source/blender/draw/intern/draw_cache_impl_curve.c b/source/blender/draw/intern/draw_cache_impl_curve.c index 73e0ff7ef83..b93c782a5b9 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.c +++ b/source/blender/draw/intern/draw_cache_impl_curve.c @@ -307,7 +307,7 @@ static int curve_render_data_normal_len_get(const CurveRenderData *rdata) return rdata->normal.len; } -static void curve_cd_calc_used_gpu_layers(int *cd_layers, +static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers, struct GPUMaterial **gpumat_array, int gpumat_array_len) { @@ -334,16 +334,16 @@ static void curve_cd_calc_used_gpu_layers(int *cd_layers, switch (type) { case CD_MTFACE: - *cd_layers |= CD_MLOOPUV; + *cd_layers |= CD_MASK_MLOOPUV; break; case CD_TANGENT: - *cd_layers |= CD_TANGENT; + *cd_layers |= CD_MASK_TANGENT; break; case CD_MCOL: /* Curve object don't have Color data. */ break; case CD_ORCO: - *cd_layers |= CD_ORCO; + *cd_layers |= CD_MASK_ORCO; break; } } @@ -397,7 +397,7 @@ typedef struct CurveBatchCache { GPUIndexBuf **surf_per_mat_tris; GPUBatch **surf_per_mat; int mat_len; - int cd_used, cd_needed; + CustomDataMask cd_used, cd_needed; /* settings to determine if cache is invalid */ bool is_dirty; @@ -998,10 +998,10 @@ void DRW_curve_batch_cache_create_requested(Object *ob) if (cache->mat_len > 1) { DRW_ibo_request(cache->surf_per_mat[i], &cache->surf_per_mat_tris[i]); } - if (cache->cd_used & CD_MLOOPUV) { + if (cache->cd_used & CD_MASK_MLOOPUV) { DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_uv); } - if (cache->cd_used & CD_TANGENT) { + if (cache->cd_used & CD_MASK_TANGENT) { DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_tan); } DRW_vbo_request(cache->surf_per_mat[i], &cache->ordered.loop_pos_nor); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 0e2be993787..e8a712b6881 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -1186,7 +1186,15 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, BLI_assert(me->edit_mesh->mesh_eval_final != NULL); } - const bool is_editmode = (me->edit_mesh != NULL) && DRW_object_is_in_edit_mode(ob); + /* Don't check `DRW_object_is_in_edit_mode(ob)` here because it means the same mesh + * may draw with edit-mesh data and regular mesh data. + * In this case the custom-data layers used wont always match in `me->runtime.batch_cache`. + * If we want to display regular mesh data, we should have a separate cache for the edit-mesh. + * See T77359. */ + const bool is_editmode = (me->edit_mesh != NULL) /* && DRW_object_is_in_edit_mode(ob) */; + + /* This could be set for paint mode too, currently it's only used for edit-mode. */ + const bool is_mode_active = is_editmode && DRW_object_is_in_edit_mode(ob); DRWBatchFlag batch_requested = cache->batch_requested; cache->batch_requested = 0; @@ -1507,6 +1515,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, me, is_editmode, is_paint_mode, + is_mode_active, ob->obmat, false, true, @@ -1524,6 +1533,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, me, is_editmode, is_paint_mode, + is_mode_active, ob->obmat, false, false, @@ -1540,6 +1550,7 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, me, is_editmode, is_paint_mode, + is_mode_active, ob->obmat, true, false, @@ -1548,6 +1559,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, scene, ts, use_hide); + /* TODO(jbakker): Work-around for threading issues in 2.90. See T79533, T79038. Needs to be + * solved or made permanent in 2.91. Underlying issue still needs to be researched. */ + BLI_task_graph_work_and_wait(task_graph); #ifdef DEBUG drw_mesh_batch_cache_check_available(task_graph, me); #endif diff --git a/source/blender/draw/intern/draw_common.c b/source/blender/draw/intern/draw_common.c index aac9af088de..f80b5bd71fd 100644 --- a/source/blender/draw/intern/draw_common.c +++ b/source/blender/draw/intern/draw_common.c @@ -214,10 +214,11 @@ void DRW_globals_update(void) } if (G_draw.block_ubo == NULL) { - G_draw.block_ubo = DRW_uniformbuffer_create(sizeof(GlobalsUboStorage), gb); + G_draw.block_ubo = GPU_uniformbuf_create_ex( + sizeof(GlobalsUboStorage), gb, "GlobalsUboStorage"); } - DRW_uniformbuffer_update(G_draw.block_ubo, gb); + GPU_uniformbuf_update(G_draw.block_ubo, gb); if (!G_draw.ramp) { ColorBand ramp = {0}; diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index d6402127e5e..645848e7fe0 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -205,11 +205,11 @@ struct DRW_Global { * Not needed for constant color. */ GlobalsUboStorage block; /** Define "globalsBlock" uniform for 'block'. */ - struct GPUUniformBuffer *block_ubo; + struct GPUUniformBuf *block_ubo; struct GPUTexture *ramp; struct GPUTexture *weight_ramp; - struct GPUUniformBuffer *view_ubo; + struct GPUUniformBuf *view_ubo; }; extern struct DRW_Global G_draw; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index d90d7d36ebc..da11dacefbd 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -69,7 +69,7 @@ #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_state.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_viewport.h" #include "IMB_colormanagement.h" @@ -84,6 +84,7 @@ #include "draw_color_management.h" #include "draw_manager_profiling.h" +#include "draw_manager_testing.h" #include "draw_manager_text.h" /* only for callbacks */ @@ -600,7 +601,7 @@ static void drw_viewport_var_init(void) } if (G_draw.view_ubo == NULL) { - G_draw.view_ubo = DRW_uniformbuffer_create(sizeof(DRWViewUboStorage), NULL); + G_draw.view_ubo = GPU_uniformbuf_create_ex(sizeof(DRWViewUboStorage), NULL, "G_draw.view_ubo"); } if (DST.draw_list == NULL) { @@ -909,7 +910,7 @@ void DRW_cache_free_old_batches(Main *bmain) for (scene = bmain->scenes.first; scene; scene = scene->id.next) { for (view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); if (depsgraph == NULL) { continue; } @@ -1314,15 +1315,15 @@ void DRW_draw_callbacks_post_scene(void) /* annotations - temporary drawing buffer (3d space) */ /* XXX: Or should we use a proper draw/overlay engine for this case? */ if (do_annotations) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); /* XXX: as scene->gpd is not copied for COW yet */ ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } drw_debug_draw(); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW); /* Callback can be nasty and do whatever they want with the state. @@ -1331,11 +1332,11 @@ void DRW_draw_callbacks_post_scene(void) /* needed so gizmo isn't obscured */ if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); DRW_draw_gizmo_3d(); } - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); drw_engines_draw_text(); DRW_draw_region_info(); @@ -1343,7 +1344,7 @@ void DRW_draw_callbacks_post_scene(void) /* annotations - temporary drawing buffer (screenspace) */ /* XXX: Or should we use a proper draw/overlay engine for this case? */ if (((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0) && (do_annotations)) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); /* XXX: as scene->gpd is not copied for COW yet */ ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, false); } @@ -1351,18 +1352,18 @@ void DRW_draw_callbacks_post_scene(void) if ((v3d->gizmo_flag & V3D_GIZMO_HIDE) == 0) { /* Draw 2D after region info so we can draw on top of the camera passepartout overlay. * 'DRW_draw_region_info' sets the projection in pixel-space. */ - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); DRW_draw_gizmo_2d(); } if (G.debug_value > 20 && G.debug_value < 30) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); /* local coordinate visible rect inside region, to accommodate overlapping ui */ const rcti *rect = ED_region_visible_rect(DST.draw_ctx.region); DRW_stats_draw(rect); } - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } } @@ -1593,7 +1594,6 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, * be to do that in the colormanagmeent shader. */ GPU_offscreen_bind(ofs, false); GPU_clear_color(0.0f, 0.0f, 0.0f, 1.0f); - GPU_clear(GPU_COLOR_BIT); /* Premult Alpha over black background. */ GPU_blend(GPU_BLEND_ALPHA_PREMULT); } @@ -1703,7 +1703,7 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph GPU_viewport_free(DST.viewport); DRW_state_reset(); - glDisable(GL_DEPTH_TEST); + GPU_depth_test(GPU_DEPTH_NONE); /* Restore Drawing area. */ GPU_framebuffer_restore(); @@ -2438,7 +2438,7 @@ void DRW_draw_depth_object( GPU_framebuffer_bind(fbl->depth_only_fb); GPU_framebuffer_clear_depth(fbl->depth_only_fb, 1.0f); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); const float(*world_clip_planes)[4] = NULL; if (RV3D_CLIPPING_ENABLED(v3d, rv3d)) { @@ -2485,7 +2485,7 @@ void DRW_draw_depth_object( } GPU_matrix_set(rv3d->viewmat); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_framebuffer_restore(); DRW_opengl_context_disable(); } @@ -2888,6 +2888,8 @@ void DRW_gpu_render_context_disable(void *UNUSED(re_gpu_context)) GPU_context_active_set(NULL); } +/** \} */ + #ifdef WITH_XR_OPENXR /* XXX @@ -2923,4 +2925,17 @@ void DRW_xr_drawing_end(void) } #endif + +/** \name Internal testing API for gtests + * \{ */ + +#ifdef WITH_OPENGL_DRAW_TESTS + +void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg) +{ + DST.draw_ctx.sh_cfg = sh_cfg; +} + +#endif + /** \} */ diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index d15a55e7bef..c0bcb0e679f 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -38,7 +38,7 @@ #include "GPU_drawlist.h" #include "GPU_framebuffer.h" #include "GPU_shader.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_viewport.h" #include "draw_instance_data.h" @@ -308,8 +308,8 @@ struct DRWUniform { }; /* DRW_UNIFORM_BLOCK */ union { - GPUUniformBuffer *block; - GPUUniformBuffer **block_ref; + GPUUniformBuf *block; + GPUUniformBuf **block_ref; }; /* DRW_UNIFORM_FLOAT_COPY */ float fvalue[4]; diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index c12b4a96488..afea820b057 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -48,6 +48,7 @@ #include "GPU_buffers.h" #include "GPU_material.h" +#include "GPU_uniform_buffer.h" #include "intern/gpu_codegen.h" @@ -85,27 +86,12 @@ static void draw_call_sort(DRWCommand *array, DRWCommand *array_tmp, int array_l memcpy(array, array_tmp, sizeof(*array) * array_len); } -GPUUniformBuffer *DRW_uniformbuffer_create(int size, const void *data) -{ - return GPU_uniformbuffer_create(size, data, NULL); -} - -void DRW_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) -{ - GPU_uniformbuffer_update(ubo, data); -} - -void DRW_uniformbuffer_free(GPUUniformBuffer *ubo) -{ - GPU_uniformbuffer_free(ubo); -} - void drw_resource_buffer_finish(ViewportMemoryPool *vmempool) { int chunk_id = DRW_handle_chunk_get(&DST.resource_handle); int elem_id = DRW_handle_id_get(&DST.resource_handle); int ubo_len = 1 + chunk_id - ((elem_id == 0) ? 1 : 0); - size_t list_size = sizeof(GPUUniformBuffer *) * ubo_len; + size_t list_size = sizeof(GPUUniformBuf *) * ubo_len; /* TODO find a better system. currently a lot of obinfos UBO are going to be unused * if not rendering with Eevee. */ @@ -118,8 +104,8 @@ void drw_resource_buffer_finish(ViewportMemoryPool *vmempool) /* Remove unnecessary buffers */ for (int i = ubo_len; i < vmempool->ubo_len; i++) { - GPU_uniformbuffer_free(vmempool->matrices_ubo[i]); - GPU_uniformbuffer_free(vmempool->obinfos_ubo[i]); + GPU_uniformbuf_free(vmempool->matrices_ubo[i]); + GPU_uniformbuf_free(vmempool->obinfos_ubo[i]); } if (ubo_len != vmempool->ubo_len) { @@ -133,15 +119,13 @@ void drw_resource_buffer_finish(ViewportMemoryPool *vmempool) void *data_obmat = BLI_memblock_elem_get(vmempool->obmats, i, 0); void *data_infos = BLI_memblock_elem_get(vmempool->obinfos, i, 0); if (vmempool->matrices_ubo[i] == NULL) { - vmempool->matrices_ubo[i] = GPU_uniformbuffer_create( - sizeof(DRWObjectMatrix) * DRW_RESOURCE_CHUNK_LEN, data_obmat, NULL); - vmempool->obinfos_ubo[i] = GPU_uniformbuffer_create( - sizeof(DRWObjectInfos) * DRW_RESOURCE_CHUNK_LEN, data_infos, NULL); - } - else { - GPU_uniformbuffer_update(vmempool->matrices_ubo[i], data_obmat); - GPU_uniformbuffer_update(vmempool->obinfos_ubo[i], data_infos); + vmempool->matrices_ubo[i] = GPU_uniformbuf_create(sizeof(DRWObjectMatrix) * + DRW_RESOURCE_CHUNK_LEN); + vmempool->obinfos_ubo[i] = GPU_uniformbuf_create(sizeof(DRWObjectInfos) * + DRW_RESOURCE_CHUNK_LEN); } + GPU_uniformbuf_update(vmempool->matrices_ubo[i], data_obmat); + GPU_uniformbuf_update(vmempool->obinfos_ubo[i], data_infos); } /* Aligned alloc to avoid unaligned memcpy. */ @@ -210,10 +194,10 @@ static void drw_shgroup_uniform_create_ex(DRWShadingGroup *shgroup, memcpy(uni->fvalue, value, sizeof(float) * length); break; case DRW_UNIFORM_BLOCK: - uni->block = (GPUUniformBuffer *)value; + uni->block = (GPUUniformBuf *)value; break; case DRW_UNIFORM_BLOCK_REF: - uni->block_ref = (GPUUniformBuffer **)value; + uni->block_ref = (GPUUniformBuf **)value; break; case DRW_UNIFORM_TEXTURE: uni->texture = (GPUTexture *)value; @@ -279,16 +263,14 @@ void DRW_shgroup_uniform_texture_ref(DRWShadingGroup *shgroup, const char *name, void DRW_shgroup_uniform_block(DRWShadingGroup *shgroup, const char *name, - const GPUUniformBuffer *ubo) + const GPUUniformBuf *ubo) { BLI_assert(ubo != NULL); int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name); drw_shgroup_uniform_create_ex(shgroup, loc, DRW_UNIFORM_BLOCK, ubo, 0, 0, 1); } -void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, - const char *name, - GPUUniformBuffer **ubo) +void DRW_shgroup_uniform_block_ref(DRWShadingGroup *shgroup, const char *name, GPUUniformBuf **ubo) { BLI_assert(ubo != NULL); int loc = GPU_shader_get_uniform_block_binding(shgroup->shader, name); @@ -1327,7 +1309,7 @@ void DRW_shgroup_add_material_resources(DRWShadingGroup *grp, struct GPUMaterial } } - GPUUniformBuffer *ubo = GPU_material_uniform_buffer_get(material); + GPUUniformBuf *ubo = GPU_material_uniform_buffer_get(material); if (ubo != NULL) { DRW_shgroup_uniform_block(grp, GPU_UBO_BLOCK_NAME, ubo); } diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 44e2eec04d9..8a81b3db7d8 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -78,6 +78,9 @@ typedef struct DRWCommandsState { void drw_state_set(DRWState state) { + /* Mask locked state. */ + state = (~DST.state_lock & state) | (DST.state_lock & DST.state); + if (DST.state == state) { return; } @@ -96,6 +99,9 @@ void drw_state_set(DRWState state) if (state & DRW_STATE_WRITE_COLOR) { write_mask |= GPU_WRITE_COLOR; } + if (state & DRW_STATE_WRITE_STENCIL_ENABLED) { + write_mask |= GPU_WRITE_STENCIL; + } switch (state & (DRW_STATE_CULL_BACK | DRW_STATE_CULL_FRONT)) { case DRW_STATE_CULL_BACK: @@ -296,16 +302,13 @@ void DRW_state_reset(void) DRW_state_reset_ex(DRW_STATE_DEFAULT); GPU_texture_unbind_all(); - GPU_uniformbuffer_unbind_all(); + GPU_uniformbuf_unbind_all(); /* Should stay constant during the whole rendering. */ GPU_point_size(5); GPU_line_smooth(false); - /* Bypass U.pixelsize factor. */ - glLineWidth(1.0f); - - /* Reset blending function */ - glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + /* Bypass U.pixelsize factor by using a factor of 0.0f. Will be clamped to 1.0f. */ + GPU_line_width(0.0f); } /** \} */ @@ -608,11 +611,7 @@ static bool ubo_bindings_validate(DRWShadingGroup *shgroup) DRWPass *parent_pass = DRW_memblock_elem_from_handle(DST.vmempool->passes, &shgroup->pass_handle); - printf("Pass : %s, Shader : %s, Block : %s, Binding %d\n", - parent_pass->name, - shgroup->shader->name, - blockname, - binding); + printf("Pass : %s, Block : %s, Binding %d\n", parent_pass->name, blockname, binding); } } # endif @@ -651,18 +650,18 @@ static void draw_update_uniforms(DRWShadingGroup *shgroup, GPU_texture_bind_ex(*uni->texture_ref, uni->sampler_state, uni->location, false); break; case DRW_UNIFORM_BLOCK: - GPU_uniformbuffer_bind(uni->block, uni->location); + GPU_uniformbuf_bind(uni->block, uni->location); break; case DRW_UNIFORM_BLOCK_REF: - GPU_uniformbuffer_bind(*uni->block_ref, uni->location); + GPU_uniformbuf_bind(*uni->block_ref, uni->location); break; case DRW_UNIFORM_BLOCK_OBMATS: state->obmats_loc = uni->location; - GPU_uniformbuffer_bind(DST.vmempool->matrices_ubo[0], uni->location); + GPU_uniformbuf_bind(DST.vmempool->matrices_ubo[0], uni->location); break; case DRW_UNIFORM_BLOCK_OBINFOS: state->obinfos_loc = uni->location; - GPU_uniformbuffer_bind(DST.vmempool->obinfos_ubo[0], uni->location); + GPU_uniformbuf_bind(DST.vmempool->obinfos_ubo[0], uni->location); break; case DRW_UNIFORM_RESOURCE_CHUNK: state->chunkid_loc = uni->location; @@ -762,13 +761,8 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa /* Front face is not a resource but it is inside the resource handle. */ bool neg_scale = DRW_handle_negative_scale_get(handle); if (neg_scale != state->neg_scale) { - if (DST.view_active->is_inverted) { - glFrontFace(neg_scale ? GL_CCW : GL_CW); - } - else { - glFrontFace(neg_scale ? GL_CW : GL_CCW); - } state->neg_scale = neg_scale; + GPU_front_facing(neg_scale != DST.view_active->is_inverted); } int chunk = DRW_handle_chunk_get(handle); @@ -777,12 +771,12 @@ static void draw_call_resource_bind(DRWCommandsState *state, const DRWResourceHa GPU_shader_uniform_int(DST.shader, state->chunkid_loc, chunk); } if (state->obmats_loc != -1) { - GPU_uniformbuffer_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]); - GPU_uniformbuffer_bind(DST.vmempool->matrices_ubo[chunk], state->obmats_loc); + GPU_uniformbuf_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]); + GPU_uniformbuf_bind(DST.vmempool->matrices_ubo[chunk], state->obmats_loc); } if (state->obinfos_loc != -1) { - GPU_uniformbuffer_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]); - GPU_uniformbuffer_bind(DST.vmempool->obinfos_ubo[chunk], state->obinfos_loc); + GPU_uniformbuf_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]); + GPU_uniformbuf_bind(DST.vmempool->obinfos_ubo[chunk], state->obinfos_loc); } state->resource_chunk = chunk; } @@ -898,13 +892,13 @@ static void draw_call_batching_finish(DRWShadingGroup *shgroup, DRWCommandsState /* Reset state */ if (state->neg_scale) { - glFrontFace(DST.view_active->is_inverted ? GL_CW : GL_CCW); + GPU_front_facing(DST.view_active->is_inverted); } if (state->obmats_loc != -1) { - GPU_uniformbuffer_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]); + GPU_uniformbuf_unbind(DST.vmempool->matrices_ubo[state->resource_chunk]); } if (state->obinfos_loc != -1) { - GPU_uniformbuffer_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]); + GPU_uniformbuf_unbind(DST.vmempool->obinfos_ubo[state->resource_chunk]); } } @@ -934,7 +928,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_uniformbuffer_unbind_all(); + GPU_uniformbuf_unbind_all(); } } GPU_shader_bind(shgroup->shader); @@ -1070,7 +1064,7 @@ static void draw_shgroup(DRWShadingGroup *shgroup, DRWState pass_state) static void drw_update_view(void) { /* TODO(fclem) update a big UBO and only bind ranges here. */ - DRW_uniformbuffer_update(G_draw.view_ubo, &DST.view_active->storage); + GPU_uniformbuf_update(G_draw.view_ubo, &DST.view_active->storage); /* TODO get rid of this. */ DST.view_storage_cpy = DST.view_active->storage; @@ -1110,7 +1104,7 @@ static void drw_draw_pass_ex(DRWPass *pass, drw_state_validate(); if (DST.view_active->is_inverted) { - glFrontFace(GL_CW); + GPU_front_facing(true); } DRW_stats_query_start(pass->name); @@ -1147,7 +1141,7 @@ static void drw_draw_pass_ex(DRWPass *pass, /* Reset default. */ if (DST.view_active->is_inverted) { - glFrontFace(GL_CCW); + GPU_front_facing(false); } DRW_stats_query_end(); diff --git a/source/blender/draw/intern/draw_manager_testing.h b/source/blender/draw/intern/draw_manager_testing.h new file mode 100644 index 00000000000..f8b5dd5af46 --- /dev/null +++ b/source/blender/draw/intern/draw_manager_testing.h @@ -0,0 +1,39 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2016, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +/* Internal API only for test cases. */ + +#pragma once + +#include "GPU_shader.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef WITH_OPENGL_DRAW_TESTS +void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index 0dc35d44788..d01e1a51080 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -105,7 +105,7 @@ void DRW_draw_cursor(void) GPU_color_mask(true, true, true, true); GPU_depth_mask(false); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (is_cursor_visible(draw_ctx, scene, view_layer)) { int co[2]; diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc new file mode 100644 index 00000000000..b2f1020ed98 --- /dev/null +++ b/source/blender/draw/tests/shaders_test.cc @@ -0,0 +1,270 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "intern/draw_manager_testing.h" + +#include "GPU_context.h" +#include "GPU_init_exit.h" +#include "GPU_shader.h" + +#include "GHOST_C-api.h" + +#include "engines/eevee/eevee_private.h" +#include "engines/gpencil/gpencil_engine.h" +#include "engines/overlay/overlay_private.h" +#include "engines/workbench/workbench_private.h" + +/* Base class for draw test cases. It will setup and tear down the GPU part around each test. */ +class DrawTest : public ::testing::Test { + private: + GHOST_SystemHandle ghost_system; + GHOST_ContextHandle ghost_context; + GPUContext *context; + + void SetUp() override + { + ghost_system = GHOST_CreateSystem(); + ghost_context = GHOST_CreateOpenGLContext(ghost_system); + context = GPU_context_create(0); + GPU_init(); + DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT); + } + + void TearDown() override + { + GPU_exit(); + GPU_context_discard(context); + GHOST_DisposeOpenGLContext(ghost_system, ghost_context); + GHOST_DisposeSystem(ghost_system); + } +}; + +TEST_F(DrawTest, workbench_glsl_shaders) +{ + workbench_shader_library_ensure(); + DRW_draw_state_init_gtests(GPU_SHADER_CFG_DEFAULT); + + const int MAX_WPD = 6; + WORKBENCH_PrivateData wpds[MAX_WPD]; + + wpds[0].sh_cfg = GPU_SHADER_CFG_DEFAULT; + wpds[0].shading.light = V3D_LIGHTING_FLAT; + wpds[1].sh_cfg = GPU_SHADER_CFG_DEFAULT; + wpds[1].shading.light = V3D_LIGHTING_MATCAP; + wpds[2].sh_cfg = GPU_SHADER_CFG_DEFAULT; + wpds[2].shading.light = V3D_LIGHTING_STUDIO; + wpds[3].sh_cfg = GPU_SHADER_CFG_CLIPPED; + wpds[3].shading.light = V3D_LIGHTING_FLAT; + wpds[4].sh_cfg = GPU_SHADER_CFG_CLIPPED; + wpds[4].shading.light = V3D_LIGHTING_MATCAP; + wpds[5].sh_cfg = GPU_SHADER_CFG_CLIPPED; + wpds[5].shading.light = V3D_LIGHTING_STUDIO; + + for (int wpd_index = 0; wpd_index < MAX_WPD; wpd_index++) { + WORKBENCH_PrivateData *wpd = &wpds[wpd_index]; + EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_MESH), nullptr); + EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_HAIR), nullptr); + EXPECT_NE(workbench_shader_opaque_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_MESH, false), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_MESH, true), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_HAIR, false), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_HAIR, true), nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, false), + nullptr); + EXPECT_NE(workbench_shader_opaque_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, true), + nullptr); + EXPECT_NE(workbench_shader_composite_get(wpd), nullptr); + EXPECT_NE(workbench_shader_merge_infront_get(wpd), nullptr); + + EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_MESH), nullptr); + EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_HAIR), nullptr); + EXPECT_NE(workbench_shader_transparent_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD), nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_MESH, false), + nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_MESH, true), nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_HAIR, false), + nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_HAIR, true), nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, false), + nullptr); + EXPECT_NE(workbench_shader_transparent_image_get(wpd, WORKBENCH_DATATYPE_POINTCLOUD, true), + nullptr); + EXPECT_NE(workbench_shader_transparent_resolve_get(wpd), nullptr); + } + + EXPECT_NE(workbench_shader_shadow_pass_get(false), nullptr); + EXPECT_NE(workbench_shader_shadow_pass_get(true), nullptr); + EXPECT_NE(workbench_shader_shadow_fail_get(false, false), nullptr); + EXPECT_NE(workbench_shader_shadow_fail_get(false, true), nullptr); + EXPECT_NE(workbench_shader_shadow_fail_get(true, false), nullptr); + EXPECT_NE(workbench_shader_shadow_fail_get(true, true), nullptr); + + /* NOTE: workbench_shader_cavity_get(false, false) isn't a valid option. */ + EXPECT_NE(workbench_shader_cavity_get(false, true), nullptr); + EXPECT_NE(workbench_shader_cavity_get(true, false), nullptr); + EXPECT_NE(workbench_shader_cavity_get(true, true), nullptr); + EXPECT_NE(workbench_shader_outline_get(), nullptr); + + EXPECT_NE(workbench_shader_antialiasing_accumulation_get(), nullptr); + EXPECT_NE(workbench_shader_antialiasing_get(0), nullptr); + EXPECT_NE(workbench_shader_antialiasing_get(1), nullptr); + EXPECT_NE(workbench_shader_antialiasing_get(2), nullptr); + + EXPECT_NE(workbench_shader_volume_get(false, false, false, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, false, false, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, false, true, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, false, true, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, true, false, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, true, false, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, true, true, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(false, true, true, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, false, false, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, false, false, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, false, true, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, false, true, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, true, false, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, true, false, true), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, true, true, false), nullptr); + EXPECT_NE(workbench_shader_volume_get(true, true, true, true), nullptr); + + GPUShader *dof_prepare_sh; + GPUShader *dof_downsample_sh; + GPUShader *dof_blur1_sh; + GPUShader *dof_blur2_sh; + GPUShader *dof_resolve_sh; + workbench_shader_depth_of_field_get( + &dof_prepare_sh, &dof_downsample_sh, &dof_blur1_sh, &dof_blur2_sh, &dof_resolve_sh); + EXPECT_NE(dof_prepare_sh, nullptr); + EXPECT_NE(dof_downsample_sh, nullptr); + EXPECT_NE(dof_blur1_sh, nullptr); + EXPECT_NE(dof_blur2_sh, nullptr); + EXPECT_NE(dof_resolve_sh, nullptr); + + workbench_shader_free(); +} + +TEST_F(DrawTest, gpencil_glsl_shaders) +{ + EXPECT_NE(GPENCIL_shader_antialiasing(0), nullptr); + EXPECT_NE(GPENCIL_shader_antialiasing(1), nullptr); + EXPECT_NE(GPENCIL_shader_antialiasing(2), nullptr); + + EXPECT_NE(GPENCIL_shader_geometry_get(), nullptr); + EXPECT_NE(GPENCIL_shader_layer_blend_get(), nullptr); + EXPECT_NE(GPENCIL_shader_mask_invert_get(), nullptr); + EXPECT_NE(GPENCIL_shader_depth_merge_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_blur_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_colorize_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_composite_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_transform_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_glow_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_pixelize_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_rim_get(), nullptr); + EXPECT_NE(GPENCIL_shader_fx_shadow_get(), nullptr); + + GPENCIL_shader_free(); +} + +TEST_F(DrawTest, overlay_glsl_shaders) +{ + for (int i = 0; i < 2; i++) { + eGPUShaderConfig sh_cfg = i == 0 ? GPU_SHADER_CFG_DEFAULT : GPU_SHADER_CFG_CLIPPED; + DRW_draw_state_init_gtests(sh_cfg); + EXPECT_NE(OVERLAY_shader_antialiasing(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_degrees_of_freedom_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_degrees_of_freedom_solid(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_envelope(false), nullptr); + EXPECT_NE(OVERLAY_shader_armature_envelope(true), nullptr); + EXPECT_NE(OVERLAY_shader_armature_shape(false), nullptr); + EXPECT_NE(OVERLAY_shader_armature_shape(true), nullptr); + EXPECT_NE(OVERLAY_shader_armature_shape_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_sphere(false), nullptr); + EXPECT_NE(OVERLAY_shader_armature_sphere(true), nullptr); + EXPECT_NE(OVERLAY_shader_armature_stick(), nullptr); + EXPECT_NE(OVERLAY_shader_armature_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_background(), nullptr); + EXPECT_NE(OVERLAY_shader_clipbound(), nullptr); + EXPECT_NE(OVERLAY_shader_depth_only(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_curve_handle(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_curve_point(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_curve_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_gpencil_guide_point(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_gpencil_point(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_gpencil_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_lattice_point(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_lattice_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_analysis(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_edge(false), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_edge(true), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_face(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_facedot(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_normal(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_skin_root(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_mesh_vert(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_particle_strand(), nullptr); + EXPECT_NE(OVERLAY_shader_edit_particle_point(), nullptr); + EXPECT_NE(OVERLAY_shader_extra(false), nullptr); + EXPECT_NE(OVERLAY_shader_extra(true), nullptr); + EXPECT_NE(OVERLAY_shader_extra_groundline(), nullptr); + EXPECT_NE(OVERLAY_shader_extra_wire(false, false), nullptr); + EXPECT_NE(OVERLAY_shader_extra_wire(false, true), nullptr); + EXPECT_NE(OVERLAY_shader_extra_wire(true, false), nullptr); + EXPECT_NE(OVERLAY_shader_extra_wire(true, true), nullptr); + EXPECT_NE(OVERLAY_shader_extra_loose_point(), nullptr); + EXPECT_NE(OVERLAY_shader_extra_point(), nullptr); + EXPECT_NE(OVERLAY_shader_facing(), nullptr); + EXPECT_NE(OVERLAY_shader_gpencil_canvas(), nullptr); + EXPECT_NE(OVERLAY_shader_grid(), nullptr); + EXPECT_NE(OVERLAY_shader_image(), nullptr); + EXPECT_NE(OVERLAY_shader_motion_path_line(), nullptr); + EXPECT_NE(OVERLAY_shader_motion_path_vert(), nullptr); + EXPECT_NE(OVERLAY_shader_uniform_color(), nullptr); + EXPECT_NE(OVERLAY_shader_outline_prepass(false), nullptr); + EXPECT_NE(OVERLAY_shader_outline_prepass(true), nullptr); + EXPECT_NE(OVERLAY_shader_outline_prepass_gpencil(), nullptr); + EXPECT_NE(OVERLAY_shader_outline_prepass_pointcloud(), nullptr); + EXPECT_NE(OVERLAY_shader_extra_grid(), nullptr); + EXPECT_NE(OVERLAY_shader_outline_detect(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_face(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_point(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_texture(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_vertcol(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_weight(), nullptr); + EXPECT_NE(OVERLAY_shader_paint_wire(), nullptr); + EXPECT_NE(OVERLAY_shader_particle_dot(), nullptr); + EXPECT_NE(OVERLAY_shader_particle_shape(), nullptr); + EXPECT_NE(OVERLAY_shader_sculpt_mask(), nullptr); + EXPECT_NE(OVERLAY_shader_volume_velocity(false), nullptr); + EXPECT_NE(OVERLAY_shader_volume_velocity(true), nullptr); + EXPECT_NE(OVERLAY_shader_wireframe(false), nullptr); + EXPECT_NE(OVERLAY_shader_wireframe(true), nullptr); + EXPECT_NE(OVERLAY_shader_wireframe_select(), nullptr); + EXPECT_NE(OVERLAY_shader_xray_fade(), nullptr); + } + + OVERLAY_shader_free(); +} + +TEST_F(DrawTest, eevee_glsl_shaders_static) +{ + EEVEE_shaders_lightprobe_shaders_init(); + EEVEE_shaders_material_shaders_init(); + + EXPECT_NE(EEVEE_shaders_probe_filter_glossy_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_filter_diffuse_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_filter_visibility_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_grid_fill_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_planar_downsample_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_studiolight_probe_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_studiolight_background_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_cube_display_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_grid_display_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_probe_planar_display_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_update_noise_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_velocity_resolve_sh_get(), nullptr); + EXPECT_NE(EEVEE_shaders_taa_resolve_sh_get(EFFECT_TAA), nullptr); + EXPECT_NE(EEVEE_shaders_taa_resolve_sh_get(EFFECT_TAA_REPROJECT), nullptr); + + EEVEE_shaders_free(); +}
\ No newline at end of file diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index 8280b58c21a..ddd389a5348 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -1555,7 +1555,7 @@ static size_t animfilter_nla(bAnimContext *UNUSED(ac), next = nlt->next; } - /* if we're in NLA-tweakmode, don't show this track if it was disabled + /* If we're in NLA-tweak-mode, don't show this track if it was disabled * (due to tweaking) for now: * - active track should still get shown though (even though it has disabled flag set) */ diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index a737916e9a2..2efb7315b89 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -304,6 +304,10 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* Inverse transform for all selected armatures in this object, + * See #object_join_exec for detailed comment on why the safe version is used. */ + invert_m4_m4_safe_ortho(oimat, ob_active->obmat); + /* Get edit-bones of active armature to add edit-bones to */ ED_armature_to_edit(arm); @@ -334,7 +338,6 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op) // BASACT->flag &= ~OB_MODE_POSE; /* Find the difference matrix */ - invert_m4_m4(oimat, ob_active->obmat); mul_m4_m4m4(mat, oimat, ob_iter->obmat); /* Copy bones and posechannels from the object to the edit armature */ @@ -435,6 +438,7 @@ int ED_armature_join_objects_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c index 75ffd31854a..ceacd3333d5 100644 --- a/source/blender/editors/armature/meshlaplacian.c +++ b/source/blender/editors/armature/meshlaplacian.c @@ -37,6 +37,7 @@ #include "BKE_bvhutils.h" #include "BKE_mesh.h" #include "BKE_mesh_runtime.h" +#include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" #include "ED_armature.h" @@ -1761,6 +1762,9 @@ void ED_mesh_deform_bind_callback(MeshDeformModifierData *mmd, memset(&mdb, 0, sizeof(MeshDeformBind)); + /* No need to support other kinds of mesh data as binding is a one-off action. */ + BKE_mesh_wrapper_ensure_mdata(cagemesh); + /* get mesh and cage mesh */ mdb.vertexcos = MEM_callocN(sizeof(float[3]) * totvert, "MeshDeformCos"); mdb.totvert = totvert; diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index d113e0693a4..fb102574a85 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -2172,12 +2172,22 @@ bool ed_editnurb_extrude_flag(EditNurb *editnurb, const short flag) return ok; } +static void calc_duplicate_actnurb(const ListBase *editnurb, const ListBase *newnurb, Curve *cu) +{ + cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb); +} + static bool calc_duplicate_actvert( const ListBase *editnurb, const ListBase *newnurb, Curve *cu, int start, int end, int vert) { + if (cu->actvert == -1) { + calc_duplicate_actnurb(editnurb, newnurb, cu); + return true; + } + if ((start <= cu->actvert) && (end > cu->actvert)) { + calc_duplicate_actnurb(editnurb, newnurb, cu); cu->actvert = vert; - cu->actnu = BLI_listbase_count(editnurb) + BLI_listbase_count(newnurb); return true; } return false; @@ -2427,26 +2437,31 @@ static void adduplicateflagNurb( } if (cu->actnu == i) { - for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { - starta = b * nu->pntsu + a; - if (calc_duplicate_actvert(editnurb, - newnurb, - cu, - cu->actvert, - starta, - cu->actvert % nu->pntsu + newu + - b * newnu->pntsu)) { - /* actvert in cyclicu selection */ - break; - } - if (calc_duplicate_actvert(editnurb, - newnurb, - cu, - starta, - starta + newu, - cu->actvert - starta + b * newnu->pntsu)) { - /* actvert in 'current' iteration selection */ - break; + if (cu->actvert == -1) { + calc_duplicate_actnurb(editnurb, newnurb, cu); + } + else { + for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { + starta = b * nu->pntsu + a; + if (calc_duplicate_actvert(editnurb, + newnurb, + cu, + cu->actvert, + starta, + cu->actvert % nu->pntsu + newu + + b * newnu->pntsu)) { + /* actvert in cyclicu selection */ + break; + } + if (calc_duplicate_actvert(editnurb, + newnurb, + cu, + starta, + starta + newu, + cu->actvert - starta + b * newnu->pntsu)) { + /* actvert in 'current' iteration selection */ + break; + } } } } @@ -2474,16 +2489,21 @@ static void adduplicateflagNurb( /* general case if not handled by cyclicu or cyclicv */ if (cu->actnu == i) { - for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { - starta = b * nu->pntsu + a; - if (calc_duplicate_actvert(editnurb, - newnurb, - cu, - starta, - starta + newu, - cu->actvert - (a / nu->pntsu * nu->pntsu + diffa + - (starta % nu->pntsu)))) { - break; + if (cu->actvert == -1) { + calc_duplicate_actnurb(editnurb, newnurb, cu); + } + else { + for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { + starta = b * nu->pntsu + a; + if (calc_duplicate_actvert(editnurb, + newnurb, + cu, + starta, + starta + newu, + cu->actvert - (a / nu->pntsu * nu->pntsu + diffa + + (starta % nu->pntsu)))) { + break; + } } } } @@ -2510,15 +2530,20 @@ static void adduplicateflagNurb( /* check for actvert in the unused cyclicuv selection */ if (cu->actnu == i) { - for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { - starta = b * nu->pntsu; - if (calc_duplicate_actvert(editnurb, - newnurb, - cu, - starta, - starta + newu, - cu->actvert - (diffa + (starta % nu->pntsu)))) { - break; + if (cu->actvert == -1) { + calc_duplicate_actnurb(editnurb, newnurb, cu); + } + else { + for (b = 0, diffa = 0; b < newv; b++, diffa += nu->pntsu - newu) { + starta = b * nu->pntsu; + if (calc_duplicate_actvert(editnurb, + newnurb, + cu, + starta, + starta + newu, + cu->actvert - (diffa + (starta % nu->pntsu)))) { + break; + } } } } @@ -6923,8 +6948,9 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op) BLI_listbase_clear(&tempbase); - /* trasnform all selected curves inverse in obact */ - invert_m4_m4(imat, ob_active->obmat); + /* Inverse transform for all selected curves in this object, + * See #object_join_exec for detailed comment on why the safe version is used. */ + invert_m4_m4_safe_ortho(imat, ob_active->obmat); CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) { if (ob_iter->type == ob_active->type) { @@ -6988,6 +7014,7 @@ int ED_curve_join_objects_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c index 2dac273501d..889041daacf 100644 --- a/source/blender/editors/curve/editcurve_paint.c +++ b/source/blender/editors/curve/editcurve_paint.c @@ -419,7 +419,7 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); GPU_line_width(3.0f); @@ -441,7 +441,7 @@ static void curve_draw_stroke_3d(const struct bContext *UNUSED(C), immEnd(); /* Reset defaults */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); GPU_blend(GPU_BLEND_NONE); GPU_line_smooth(false); diff --git a/source/blender/editors/gizmo_library/gizmo_draw_utils.c b/source/blender/editors/gizmo_library/gizmo_draw_utils.c index 033673a99a8..2896aa25930 100644 --- a/source/blender/editors/gizmo_library/gizmo_draw_utils.c +++ b/source/blender/editors/gizmo_library/gizmo_draw_utils.c @@ -84,13 +84,13 @@ void wm_gizmo_geometryinfo_draw(const GizmoGeomInfo *info, * since it causes issues leaving the GL state modified. */ #if 0 GPU_face_culling(GPU_CULL_BACK); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); #endif GPU_batch_draw(batch); #if 0 - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_face_culling(GPU_CULL_NONE); #endif diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c index 654d1b87918..b6cbbe7712b 100644 --- a/source/blender/editors/gpencil/annotate_draw.c +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -556,7 +556,7 @@ static void annotation_draw_strokes(const bGPDframe *gpf, const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); if (no_xray) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); /* first arg is normally rv3d->dist, but this isn't * available here and seems to work quite well without */ @@ -574,7 +574,7 @@ static void annotation_draw_strokes(const bGPDframe *gpf, } if (no_xray) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_polygon_offset(0.0f, 0.0f); } diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 9d11c1c2a25..93767127cc7 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -348,7 +348,7 @@ static void gpencil_draw_strokes(tGPDdraw *tgpw) const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY); if (no_xray) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); /* first arg is normally rv3d->dist, but this isn't * available here and seems to work quite well without */ @@ -393,7 +393,7 @@ static void gpencil_draw_strokes(tGPDdraw *tgpw) } } if (no_xray) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_polygon_offset(0.0f, 0.0f); } diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 348fb614977..6c003b85edd 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -2819,7 +2819,10 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) sub_v3_v3v3(offset_global, ob_active->loc, ob_iter->obmat[3]); copy_m3_m4(bmat, ob_active->obmat); - invert_m3_m3(imat, bmat); + + /* Inverse transform for all selected curves in this object, + * See #object_join_exec for detailed comment on why the safe version is used. */ + invert_m3_m3_safe_ortho(imat, bmat); mul_m3_v3(imat, offset_global); mul_v3_m3v3(offset_local, imat, offset_global); @@ -2830,7 +2833,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) /* recalculate all stroke points */ BKE_gpencil_parent_matrix_get(depsgraph, ob_iter, gpl_src, diff_mat); - invert_m4_m4(inverse_diff_mat, diff_mat); + invert_m4_m4_safe_ortho(inverse_diff_mat, diff_mat); Material *ma_src = NULL; LISTBASE_FOREACH (bGPDframe *, gpf, &gpl_new->frames) { @@ -2910,6 +2913,7 @@ int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) DEG_relations_tag_update(bmain); /* because we removed object(s) */ WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index ae9146b3a6a..247cc218c2f 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -454,8 +454,9 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) GPU_matrix_push(); GPU_matrix_identity_set(); + GPU_depth_mask(true); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT); + GPU_clear_depth(1.0f); ED_view3d_update_viewmat( tgpf->depsgraph, tgpf->scene, tgpf->v3d, tgpf->region, NULL, winmat, NULL, true); @@ -467,6 +468,8 @@ static bool gpencil_render_offscreen(tGPDfill *tgpf) const float ink[4] = {1.0f, 0.0f, 0.0f, 1.0f}; gpencil_draw_datablock(tgpf, ink); + GPU_depth_mask(false); + GPU_matrix_pop_projection(); GPU_matrix_pop(); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index ad46dada0c9..dd7ca5c65a4 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -81,18 +81,12 @@ void ED_region_tag_refresh_ui(struct ARegion *region); void ED_region_tag_redraw_editor_overlays(struct ARegion *region); void ED_region_panels_init(struct wmWindowManager *wm, struct ARegion *region); -void ED_region_panels_ex(const struct bContext *C, - struct ARegion *region, - const char *contexts[], - int contextnr, - const bool vertical); +void ED_region_panels_ex(const struct bContext *C, struct ARegion *region, const char *contexts[]); void ED_region_panels(const struct bContext *C, struct ARegion *region); void ED_region_panels_layout_ex(const struct bContext *C, struct ARegion *region, struct ListBase *paneltypes, const char *contexts[], - int contextnr, - const bool vertical, const char *category_override); void ED_region_panels_layout(const struct bContext *C, struct ARegion *region); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 5d936cdfaa3..1fa9ed0b2c0 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1667,19 +1667,13 @@ void UI_panels_end(const struct bContext *C, struct ARegion *region, int *r_x, i void UI_panels_draw(const struct bContext *C, struct ARegion *region); struct Panel *UI_panel_find_by_type(struct ListBase *lb, struct PanelType *pt); -struct Panel *UI_panel_begin(struct ScrArea *area, - struct ARegion *region, +struct Panel *UI_panel_begin(struct ARegion *region, struct ListBase *lb, uiBlock *block, struct PanelType *pt, struct Panel *panel, bool *r_open); -void UI_panel_end(const struct ScrArea *area, - const struct ARegion *region, - uiBlock *block, - int width, - int height, - bool open); +void UI_panel_end(const struct ARegion *region, uiBlock *block, int width, int height, bool open); void UI_panels_scale(struct ARegion *region, float new_width); void UI_panel_label_offset(struct uiBlock *block, int *r_x, int *r_y); @@ -1709,8 +1703,7 @@ struct PointerRNA *UI_region_panel_custom_data_under_cursor(const struct bContex void UI_panel_custom_data_set(struct Panel *panel, struct PointerRNA *custom_data); /* Polyinstantiated panels for representing a list of data. */ -struct Panel *UI_panel_add_instanced(struct ScrArea *area, - struct ARegion *region, +struct Panel *UI_panel_add_instanced(struct ARegion *region, struct ListBase *panels, char *panel_idname, int list_index, diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index 3f548f98e97..31cb62117c5 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -262,7 +262,6 @@ typedef enum ThemeColorID { TH_PAINT_CURVE_PIVOT, TH_UV_SHADOW, - TH_UV_OTHERS, TH_FREESTYLE_EDGE_MARK, TH_FREESTYLE_FACE_MARK, @@ -431,12 +430,9 @@ void UI_GetColorPtrBlendShade3ubv(const unsigned char cp1[3], // (for anything fancy use UI_GetThemeColor[Fancy] then BLF_color) void UI_FontThemeColor(int fontid, int colorid); -// clear the openGL ClearColor using the input colorid +// clear the framebuffer using the input colorid void UI_ThemeClearColor(int colorid); -// clear the openGL ClearColor using the input colorid using optional transparency -void UI_ThemeClearColorAlpha(int colorid, float alpha); - // internal (blender) usage only, for init and set active void UI_SetTheme(int spacetype, int regionid); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index e04531bb1dd..9cede126890 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -304,11 +304,11 @@ static void ui_update_flexible_spacing(const ARegion *region, uiBlock *block) /* How much the next block overlap with the current segment */ int overlap = ((i == sepr_flex_len - 1) ? buttons_width - spacers_pos[i] : (spacers_pos[i + 1] - spacers_pos[i]) / 2); - int segment_end = segment_width * (i + 1); - int spacer_end = segment_end - overlap; - int spacer_sta = spacers_pos[i] + offset; + const int segment_end = segment_width * (i + 1); + const int spacer_end = segment_end - overlap; + const int spacer_sta = spacers_pos[i] + offset; if (spacer_end > spacer_sta) { - float step = min_ff(remaining_space, spacer_end - spacer_sta); + const float step = min_ff(remaining_space, spacer_end - spacer_sta); remaining_space -= step; offset += step; } @@ -329,9 +329,9 @@ static void ui_update_window_matrix(const wmWindow *window, const ARegion *regio else { /* No subwindow created yet, for menus for example, so we use the main * window instead, since buttons are created there anyway. */ - int width = WM_window_pixels_x(window); - int height = WM_window_pixels_y(window); - rcti winrct = {0, width - 1, 0, height - 1}; + const int width = WM_window_pixels_x(window); + const int height = WM_window_pixels_y(window); + const rcti winrct = {0, width - 1, 0, height - 1}; wmGetProjectionMatrix(block->winmat, &winrct); block->aspect = 2.0f / fabsf(width * block->winmat[0][0]); @@ -1130,7 +1130,7 @@ static bool ui_but_event_operator_string_from_menu(const bContext *C, IDProperty *prop_menu; /* annoying, create a property */ - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; prop_menu = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant */ IDP_AddToGroup(prop_menu, IDP_NewString(mt->idname, "name", sizeof(mt->idname))); @@ -1156,7 +1156,7 @@ static bool ui_but_event_operator_string_from_panel(const bContext *C, IDProperty *prop_panel; /* annoying, create a property */ - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; prop_panel = IDP_New(IDP_GROUP, &val, __func__); /* dummy, name is unimportant */ IDP_AddToGroup(prop_panel, IDP_NewString(pt->idname, "name", sizeof(pt->idname))); IDP_AddToGroup(prop_panel, @@ -1361,7 +1361,7 @@ static bool ui_but_event_property_operator_string(const bContext *C, /* create a property to host the "datapath" property we're sending to the operators */ IDProperty *prop_path; - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; prop_path = IDP_New(IDP_GROUP, &val, __func__); if (data_path) { IDP_AddToGroup(prop_path, IDP_NewString(data_path, "data_path", strlen(data_path) + 1)); @@ -1370,11 +1370,11 @@ static bool ui_but_event_property_operator_string(const bContext *C, const EnumPropertyItem *item; bool free; RNA_property_enum_items((bContext *)C, ptr, prop, &item, NULL, &free); - int index = RNA_enum_from_value(item, prop_enum_value); + const int index = RNA_enum_from_value(item, prop_enum_value); if (index != -1) { IDProperty *prop_value; if (prop_enum_value_is_int) { - int value = item[index].value; + const int value = item[index].value; prop_value = IDP_New(IDP_INT, &(IDPropertyTemplate){ .i = value, @@ -1656,7 +1656,7 @@ static PredefinedExtraOpIconType ui_but_icon_extra_get(uiBut *but) */ static void ui_but_predefined_extra_operator_icons_add(uiBut *but) { - PredefinedExtraOpIconType extra_icon = ui_but_icon_extra_get(but); + const PredefinedExtraOpIconType extra_icon = ui_but_icon_extra_get(but); wmOperatorType *optype = NULL; BIFIconID icon = ICON_NONE; @@ -2047,7 +2047,7 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) /* uiBut.custom_data points to data this tab represents (e.g. workspace). * uiBut.rnapoin/prop store an active value (e.g. active workspace). */ if (RNA_property_type(but->rnaprop) == PROP_POINTER) { - PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop); + const PointerRNA active_ptr = RNA_property_pointer_get(&but->rnapoin, but->rnaprop); if (active_ptr.data == but->custom_data) { is_push = true; } @@ -2518,7 +2518,7 @@ uiBut *ui_but_drag_multi_edit_get(uiBut *but) static double ui_get_but_scale_unit(uiBut *but, double value) { UnitSettings *unit = but->block->unit; - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); /* Time unit is a bit special, not handled by BKE_scene_unit_scale() for now. */ if (unit_type == PROP_UNIT_TIME) { /* WARNING - using evil_C :| */ @@ -2533,7 +2533,7 @@ void ui_but_convert_to_unit_alt_name(uiBut *but, char *str, size_t maxlen) { if (ui_but_is_unit(but)) { UnitSettings *unit = but->block->unit; - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); char *orig_str; orig_str = BLI_strdup(str); @@ -2551,7 +2551,7 @@ static void ui_get_but_string_unit( uiBut *but, char *str, int len_max, double value, bool pad, int float_precision) { UnitSettings *unit = but->block->unit; - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); int precision; if (unit->scale_length < 0.0001f) { @@ -2584,7 +2584,7 @@ static void ui_get_but_string_unit( static float ui_get_but_step_unit(uiBut *but, float step_default) { - int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); + const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); const double step_orig = step_default * UI_PRECISION_FLOAT_SCALE; /* Scaling up 'step_origg ' here is a bit arbitrary, * its just giving better scales from user POV */ @@ -2655,7 +2655,7 @@ void ui_but_string_get_ex(uiBut *but, } else if (type == PROP_ENUM) { /* RNA enum */ - int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); + const int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); if (RNA_property_enum_name(but->block->evil_C, &but->rnapoin, but->rnaprop, value, &buf)) { BLI_strncpy(str, buf, maxlen); buf = str; @@ -2774,7 +2774,7 @@ char *ui_but_string_get_dynamic(uiBut *but, int *r_str_size) } else if (type == PROP_ENUM) { /* RNA enum */ - int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); + const int value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); const char *value_id; if (!RNA_property_enum_name( but->block->evil_C, &but->rnapoin, but->rnaprop, value, &value_id)) { @@ -2845,10 +2845,10 @@ static bool ui_number_from_string(bContext *C, const char *str, double *r_value) static bool ui_number_from_string_factor(bContext *C, const char *str, double *r_value) { - int len = strlen(str); + const int len = strlen(str); if (BLI_strn_endswith(str, "%", len)) { char *str_new = BLI_strdupn(str, len - 1); - bool success = ui_number_from_string(C, str_new, r_value); + const bool success = ui_number_from_string(C, str_new, r_value); MEM_freeN(str_new); *r_value /= 100.0; return success; @@ -2864,10 +2864,10 @@ static bool ui_number_from_string_factor(bContext *C, const char *str, double *r static bool ui_number_from_string_percentage(bContext *C, const char *str, double *r_value) { - int len = strlen(str); + const int len = strlen(str); if (BLI_strn_endswith(str, "%", len)) { char *str_new = BLI_strdupn(str, len - 1); - bool success = ui_number_from_string(C, str_new, r_value); + const bool success = ui_number_from_string(C, str_new, r_value); MEM_freeN(str_new); return success; } @@ -3078,7 +3078,7 @@ static double soft_range_round_up(double value, double max) { /* round up to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, .. * checking for 0.0 prevents floating point exceptions */ - double newmax = (value != 0.0) ? pow(10.0, ceil(log(value) / M_LN10)) : 0.0; + const double newmax = (value != 0.0) ? pow(10.0, ceil(log(value) / M_LN10)) : 0.0; if (newmax * 0.2 >= max && newmax * 0.2 >= value) { return newmax * 0.2; @@ -3093,7 +3093,7 @@ static double soft_range_round_down(double value, double max) { /* round down to .., 0.1, 0.2, 0.5, 1, 2, 5, 10, 20, 50, .. * checking for 0.0 prevents floating point exceptions */ - double newmax = (value != 0.0) ? pow(10.0, floor(log(value) / M_LN10)) : 0.0; + const double newmax = (value != 0.0) ? pow(10.0, floor(log(value) / M_LN10)) : 0.0; if (newmax * 5.0 <= max && newmax * 5.0 <= value) { return newmax * 5.0; @@ -3481,6 +3481,9 @@ static void ui_but_build_drawstr_float(uiBut *but, double value) subtype = RNA_property_subtype(but->rnaprop); } + /* Change negative zero to regular zero, without altering anything else. */ + value += +0.0f; + if (value == (double)FLT_MAX) { STR_CONCAT(but->drawstr, slen, "inf"); } @@ -3488,15 +3491,15 @@ static void ui_but_build_drawstr_float(uiBut *but, double value) STR_CONCAT(but->drawstr, slen, "-inf"); } else if (subtype == PROP_PERCENTAGE) { - int prec = ui_but_calc_float_precision(but, value); + const int prec = ui_but_calc_float_precision(but, value); STR_CONCATF(but->drawstr, slen, "%.*f%%", prec, value); } else if (subtype == PROP_PIXEL) { - int prec = ui_but_calc_float_precision(but, value); + const int prec = ui_but_calc_float_precision(but, value); STR_CONCATF(but->drawstr, slen, "%.*f px", prec, value); } else if (subtype == PROP_FACTOR) { - int precision = ui_but_calc_float_precision(but, value); + const int precision = ui_but_calc_float_precision(but, value); if (U.factor_display_type == USER_FACTOR_AS_FACTOR) { STR_CONCATF(but->drawstr, slen, "%.*f", precision, value); @@ -3511,7 +3514,7 @@ static void ui_but_build_drawstr_float(uiBut *but, double value) STR_CONCAT(but->drawstr, slen, new_str); } else { - int prec = ui_but_calc_float_precision(but, value); + const int prec = ui_but_calc_float_precision(but, value); STR_CONCATF(but->drawstr, slen, "%.*f", prec, value); } } @@ -3601,12 +3604,12 @@ static void ui_but_update_ex(uiBut *but, const bool validate) /* only needed for menus in popup blocks that don't recreate buttons on redraw */ if (but->block->flag & UI_BLOCK_LOOP) { if (but->rnaprop && (RNA_property_type(but->rnaprop) == PROP_ENUM)) { - int value_enum = RNA_property_enum_get(&but->rnapoin, but->rnaprop); + const int value_enum = RNA_property_enum_get(&but->rnapoin, but->rnaprop); EnumPropertyItem item; if (RNA_property_enum_item_from_value_gettexted( but->block->evil_C, &but->rnapoin, but->rnaprop, value_enum, &item)) { - size_t slen = strlen(item.name); + const size_t slen = strlen(item.name); ui_but_string_free_internal(but); ui_but_string_set_internal(but, item.name, slen); but->icon = item.icon; @@ -4797,7 +4800,7 @@ static uiBut *uiDefButBit(uiBlock *block, float a2, const char *tip) { - int bitIdx = findBitIndex(bit); + const int bitIdx = findBitIndex(bit); if (bitIdx == -1) { return NULL; } @@ -5182,7 +5185,7 @@ static uiBut *uiDefIconButBit(uiBlock *block, float a2, const char *tip) { - int bitIdx = findBitIndex(bit); + const int bitIdx = findBitIndex(bit); if (bitIdx == -1) { return NULL; } @@ -5568,7 +5571,7 @@ static uiBut *uiDefIconTextButBit(uiBlock *block, float a2, const char *tip) { - int bitIdx = findBitIndex(bit); + const int bitIdx = findBitIndex(bit); if (bitIdx == -1) { return NULL; } @@ -6127,7 +6130,7 @@ void UI_but_unit_type_set(uiBut *but, const int unit_type) int UI_but_unit_type_get(const uiBut *but) { - int ownUnit = (int)but->unit_type; + const int ownUnit = (int)but->unit_type; /* own unit define always takes precedence over RNA provided, allowing for overriding * default value provided in RNA in a few special cases (i.e. Active Keyframe in Graph Edit) diff --git a/source/blender/editors/interface/interface_anim.c b/source/blender/editors/interface/interface_anim.c index 56df49981e0..6fb9b99632e 100644 --- a/source/blender/editors/interface/interface_anim.c +++ b/source/blender/editors/interface/interface_anim.c @@ -60,7 +60,7 @@ static FCurve *ui_but_get_fcurve( { /* for entire array buttons we check the first component, it's not perfect * but works well enough in typical cases */ - int rnaindex = (but->rnaindex == -1) ? 0 : but->rnaindex; + 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); @@ -158,7 +158,7 @@ void ui_but_anim_decorate_update_from_flag(uiButDecorator *decorator_but) return; } - int flag = but_anim->flag; + const int flag = but_anim->flag; if (flag & UI_BUT_DRIVEN) { but->icon = ICON_DECORATE_DRIVER; diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 59178e209db..9889c81ca55 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -91,7 +91,7 @@ static IDProperty *shortcut_property_from_rna(bContext *C, uiBut *but) /* Create ID property of data path, to pass to the operator. */ IDProperty *prop; - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; prop = IDP_New(IDP_GROUP, &val, __func__); IDP_AddToGroup(prop, IDP_NewString(final_data_path, "data_path", strlen(final_data_path) + 1)); @@ -517,7 +517,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) uiLayout *layout; { - uiStringInfo label = {BUT_GET_LABEL, NULL}; + const uiStringInfo label = {BUT_GET_LABEL, NULL}; /* highly unlikely getting the label ever fails */ UI_but_string_info_get(C, but, &label, NULL); @@ -548,9 +548,9 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) const PropertyType type = RNA_property_type(prop); const PropertySubType subtype = RNA_property_subtype(prop); bool is_anim = RNA_property_animateable(ptr, prop); - bool is_editable = RNA_property_editable(ptr, prop); - bool is_idprop = RNA_property_is_idprop(prop); - bool is_set = RNA_property_is_set(ptr, prop); + const bool is_editable = RNA_property_editable(ptr, prop); + const bool is_idprop = RNA_property_is_idprop(prop); + const bool is_set = RNA_property_is_set(ptr, prop); /* second slower test, * saved people finding keyframe items in menus when its not possible */ @@ -1044,7 +1044,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) if (idname != NULL) { uiBlock *block = uiLayoutGetBlock(layout); uiBut *but2; - int w = uiLayoutGetWidth(layout); + const int w = uiLayoutGetWidth(layout); wmKeyMap *km; /* We want to know if this op has a shortcut, be it hotkey or not. */ diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index ba878be3dc7..25c5e63d23e 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -622,10 +622,10 @@ void UI_draw_roundbox_shade_y(bool filled, void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]) { - int ofs_y = 4 * U.pixelsize; + const int ofs_y = 4 * U.pixelsize; GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4fv(color); @@ -644,8 +644,9 @@ void ui_draw_but_TAB_outline(const rcti *rect, uchar highlight_fade[3]) { GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); /* add a 1px offset, looks nicer */ const int minx = rect->xmin + U.pixelsize, maxx = rect->xmax - U.pixelsize; const int miny = rect->ymin + U.pixelsize, maxy = rect->ymax - U.pixelsize; @@ -741,8 +742,8 @@ void ui_draw_but_IMAGE(ARegion *UNUSED(region), return; } - int w = BLI_rcti_size_x(rect); - int h = BLI_rcti_size_y(rect); + const int w = BLI_rcti_size_x(rect); + const int h = BLI_rcti_size_y(rect); /* scissor doesn't seem to be doing the right thing...? */ # if 0 @@ -813,13 +814,13 @@ void UI_draw_safe_areas(uint pos, for (int i = 0; i < safe_len; i++) { if (safe_areas[i][0] || safe_areas[i][1]) { - float margin_x = safe_areas[i][0] * size_x_half; - float margin_y = safe_areas[i][1] * size_y_half; + const float margin_x = safe_areas[i][0] * size_x_half; + const float margin_y = safe_areas[i][1] * size_y_half; - float minx = x1 + margin_x; - float miny = y1 + margin_y; - float maxx = x2 - margin_x; - float maxy = y2 - margin_y; + const float minx = x1 + margin_x; + const float miny = y1 + margin_y; + const float maxx = x2 - margin_x; + const float maxy = y2 - margin_y; imm_draw_box_wire_2d(pos, minx, miny, maxx, maxy); } @@ -868,7 +869,7 @@ static void histogram_draw_one(float r, immBegin(GPU_PRIM_LINE_STRIP, res); for (int i = 0; i < res; i++) { - float x2 = x + i * (w / (float)res); + const float x2 = x + i * (w / (float)res); immVertex2f(pos_attr, x2, y + (data[i] * h)); } immEnd(); @@ -879,7 +880,7 @@ static void histogram_draw_one(float r, immVertex2f(pos_attr, x, y); immVertex2f(pos_attr, x, y + (data[0] * h)); for (int i = 1; i < res; i++) { - float x2 = x + i * (w / (float)res); + const float x2 = x + i * (w / (float)res); immVertex2f(pos_attr, x2, y + (data[i] * h)); immVertex2f(pos_attr, x2, y); } @@ -891,7 +892,7 @@ static void histogram_draw_one(float r, GPU_blend(GPU_BLEND_ALPHA); immBegin(GPU_PRIM_LINE_STRIP, res); for (int i = 0; i < res; i++) { - float x2 = x + i * (w / (float)res); + const float x2 = x + i * (w / (float)res); immVertex2f(pos_attr, x2, y + (data[i] * h)); } immEnd(); @@ -908,7 +909,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), const rcti *recti) { Histogram *hist = (Histogram *)but->poin; - int res = hist->x_resolution; + const int res = hist->x_resolution; const bool is_line = (hist->flag & HISTO_FLAG_LINE) != 0; rctf rect = { @@ -918,8 +919,8 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), .ymax = (float)recti->ymax - 1, }; - float w = BLI_rctf_size_x(&rect); - float h = BLI_rctf_size_y(&rect) * hist->ymax; + const float w = BLI_rctf_size_x(&rect); + const float h = BLI_rctf_size_y(&rect) * hist->ymax; GPU_blend(GPU_BLEND_ALPHA); @@ -938,7 +939,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), (rect.ymax + 1) - (rect.ymin - 1)); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1000,7 +1001,7 @@ void ui_draw_but_HISTOGRAM(ARegion *UNUSED(region), static void waveform_draw_one(float *waveform, int nbr, const float col[3]) { GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos_id = GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); GPU_vertbuf_data_alloc(vbo, nbr); @@ -1043,13 +1044,13 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), if (scopes->wavefrm_yfac < 0.5f) { scopes->wavefrm_yfac = 0.98f; } - float w = BLI_rctf_size_x(&rect) - 7; - float h = BLI_rctf_size_y(&rect) * scopes->wavefrm_yfac; - float yofs = rect.ymin + (BLI_rctf_size_y(&rect) - h) * 0.5f; - float w3 = w / 3.0f; + const float w = BLI_rctf_size_x(&rect) - 7; + const float h = BLI_rctf_size_y(&rect) * scopes->wavefrm_yfac; + const float yofs = rect.ymin + (BLI_rctf_size_y(&rect) - h) * 0.5f; + const float w3 = w / 3.0f; /* log scale for alpha */ - float alpha = scopes->wavefrm_alpha * scopes->wavefrm_alpha; + const float alpha = scopes->wavefrm_alpha * scopes->wavefrm_alpha; unit_m3(colors); @@ -1093,7 +1094,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1200,7 +1201,7 @@ void ui_draw_but_WAVEFORM(ARegion *UNUSED(region), SCOPES_WAVEFRM_YCC_601, SCOPES_WAVEFRM_YCC_709, SCOPES_WAVEFRM_YCC_JPEG)) { - int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE); + const int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE); GPU_matrix_push(); GPU_matrix_translate_2f(rect.xmin, yofs); @@ -1384,13 +1385,13 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region), .ymax = (float)recti->ymax - 1, }; - float w = BLI_rctf_size_x(&rect); - float h = BLI_rctf_size_y(&rect); - float centerx = rect.xmin + w * 0.5f; - float centery = rect.ymin + h * 0.5f; - float diam = (w < h) ? w : h; + const float w = BLI_rctf_size_x(&rect); + const float h = BLI_rctf_size_y(&rect); + const float centerx = rect.xmin + w * 0.5f; + const float centery = rect.ymin + h * 0.5f; + const float diam = (w < h) ? w : h; - float alpha = scopes->vecscope_alpha * scopes->vecscope_alpha * scopes->vecscope_alpha; + const float alpha = scopes->vecscope_alpha * scopes->vecscope_alpha * scopes->vecscope_alpha; GPU_blend(GPU_BLEND_ALPHA); @@ -1409,7 +1410,7 @@ void ui_draw_but_VECTORSCOPE(ARegion *UNUSED(region), (rect.ymax + 1) - (rect.ymin - 1)); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -1538,11 +1539,11 @@ static void ui_draw_colorband_handle(uint shdr_pos, const float min_width = 3.0f; float colf[3] = {UNPACK3(rgb)}; - float half_width = floorf(sizey / 3.5f); - float height = half_width * 1.4f; + const float half_width = floorf(sizey / 3.5f); + const float height = half_width * 1.4f; float y1 = rect->ymin + (sizey * 0.16f); - float y2 = rect->ymax; + const float y2 = rect->ymax; /* align to pixels */ x = floorf(x + 0.5f); @@ -1637,11 +1638,11 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const return; } - float x1 = rect->xmin; - float sizex = rect->xmax - x1; - float sizey = BLI_rcti_size_y(rect); - float sizey_solid = sizey * 0.25f; - float y1 = rect->ymin; + const float x1 = rect->xmin; + const float sizex = rect->xmax - x1; + const float sizey = BLI_rcti_size_y(rect); + const float sizey_solid = sizey * 0.25f; + const float y1 = rect->ymin; /* exit early if too narrow */ if (sizex <= 0) { @@ -1680,7 +1681,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const immBegin(GPU_PRIM_TRI_STRIP, (sizex + 1) * 2); for (int a = 0; a <= sizex; a++) { - float pos = ((float)a) / sizex; + const float pos = ((float)a) / sizex; BKE_colorband_evaluate(coba, pos, colf); if (display) { IMB_colormanagement_scene_linear_to_display_v3(colf, display); @@ -1700,7 +1701,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const immBegin(GPU_PRIM_TRI_STRIP, (sizex + 1) * 2); for (int a = 0; a <= sizex; a++) { - float pos = ((float)a) / sizex; + const float pos = ((float)a) / sizex; BKE_colorband_evaluate(coba, pos, colf); if (display) { IMB_colormanagement_scene_linear_to_display_v3(colf, display); @@ -1748,7 +1749,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const /* layer: draw handles */ for (int a = 0; a < coba->tot; a++, cbd++) { if (a != coba->cur) { - float pos = x1 + cbd->pos * (sizex - 1) + 1; + const float pos = x1 + cbd->pos * (sizex - 1) + 1; ui_draw_colorband_handle(pos_id, rect, pos, &cbd->r, display, false); } } @@ -1756,7 +1757,7 @@ void ui_draw_but_COLORBAND(uiBut *but, const uiWidgetColors *UNUSED(wcol), const /* layer: active handle */ if (coba->tot != 0) { cbd = &coba->data[coba->cur]; - float pos = x1 + cbd->pos * (sizex - 1) + 1; + const float pos = x1 + cbd->pos * (sizex - 1) + 1; ui_draw_colorband_handle(pos_id, rect, pos, &cbd->r, display, true); } @@ -1783,7 +1784,7 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec /* transform to button */ GPU_matrix_push(); - bool use_project_matrix = (size >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT); + const bool use_project_matrix = (size >= -GPU_MATRIX_ORTHO_CLIP_NEAR_DEFAULT); if (use_project_matrix) { GPU_matrix_push_projection(); GPU_matrix_ortho_set_z(-size, size); @@ -1804,7 +1805,7 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec /* AA circle */ GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3ubv(wcol->inner); @@ -1827,13 +1828,13 @@ void ui_draw_but_UNITVEC(uiBut *but, const uiWidgetColors *wcol, const rcti *rec static void ui_draw_but_curve_grid( uint pos, const rcti *rect, float zoomx, float zoomy, float offsx, float offsy, float step) { - float dx = step * zoomx; + const float dx = step * zoomx; float fx = rect->xmin + zoomx * (-offsx); if (fx > rect->xmin) { fx -= dx * (floorf(fx - rect->xmin)); } - float dy = step * zoomy; + const float dy = step * zoomy; float fy = rect->ymin + zoomy * (-offsy); if (fy > rect->ymin) { fy -= dy * (floorf(fy - rect->ymin)); @@ -1885,8 +1886,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, CurveMapping *cumap = (but_cumap->edit_cumap == NULL) ? (CurveMapping *)but->poin : but_cumap->edit_cumap; - float clip_size_x = BLI_rctf_size_x(&cumap->curr); - float clip_size_y = BLI_rctf_size_y(&cumap->curr); + const float clip_size_x = BLI_rctf_size_x(&cumap->curr); + const float clip_size_y = BLI_rctf_size_y(&cumap->curr); /* zero-sized curve */ if (clip_size_x == 0.0f || clip_size_y == 0.0f) { @@ -1894,10 +1895,10 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, } /* calculate offset and zoom */ - float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / clip_size_x; - float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / clip_size_y; - float offsx = cumap->curr.xmin - (1.0f / zoomx); - float offsy = cumap->curr.ymin - (1.0f / zoomy); + const float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / clip_size_x; + const float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / clip_size_y; + const float offsx = cumap->curr.xmin - (1.0f / zoomx); + const float offsy = cumap->curr.ymin - (1.0f / zoomy); /* exit early if too narrow */ if (zoomx == 0.0f) { @@ -1915,7 +1916,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, .xmax = rect->xmax, .ymax = rect->ymax, }; - rcti scissor_region = {0, region->winx, 0, region->winy}; + const rcti scissor_region = {0, region->winx, 0, region->winy}; BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new); GPU_scissor(scissor_new.xmin, scissor_new.ymin, @@ -2014,7 +2015,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, immVertex2f(pos, rect->xmin + zoomx * (hsv[0] - offsx), rect->ymax); } else if (cumap->cur == 3) { - float lum = IMB_colormanagement_get_luminance(cumap->sample); + const float lum = IMB_colormanagement_get_luminance(cumap->sample); immUniformColor3ub(240, 240, 240); immVertex2f(pos, rect->xmin + zoomx * (lum - offsx), rect->ymin); @@ -2073,8 +2074,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, immVertex2f(pos, line_range.xmin, rect->ymin); immVertex2f(pos, line_range.xmin, line_range.ymin); for (int a = 0; a <= CM_TABLE; a++) { - float fx = rect->xmin + zoomx * (cmp[a].x - offsx); - float fy = rect->ymin + zoomy * (cmp[a].y - offsy); + const float fx = rect->xmin + zoomx * (cmp[a].x - offsx); + const float fy = rect->ymin + zoomy * (cmp[a].y - offsy); immVertex2f(pos, fx, rect->ymin); immVertex2f(pos, fx, fy); } @@ -2089,8 +2090,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, immBegin(GPU_PRIM_LINE_STRIP, (CM_TABLE + 1) + 2); immVertex2f(pos, line_range.xmin, line_range.ymin); for (int a = 0; a <= CM_TABLE; a++) { - float fx = rect->xmin + zoomx * (cmp[a].x - offsx); - float fy = rect->ymin + zoomy * (cmp[a].y - offsy); + const float fx = rect->xmin + zoomx * (cmp[a].x - offsx); + const float fy = rect->ymin + zoomy * (cmp[a].y - offsy); immVertex2f(pos, fx, fy); } immVertex2f(pos, line_range.xmax, line_range.ymax); @@ -2104,7 +2105,7 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, /* The points, use aspect to make them visible on edges. */ format = immVertexFormat(); pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); /* Calculate vertex colors based on text theme. */ @@ -2123,8 +2124,8 @@ void ui_draw_but_CURVE(ARegion *region, uiBut *but, const uiWidgetColors *wcol, GPU_point_size(max_ff(1.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f))); immBegin(GPU_PRIM_POINTS, cuma->totpoint); for (int a = 0; a < cuma->totpoint; a++) { - float fx = rect->xmin + zoomx * (cmp[a].x - offsx); - float fy = rect->ymin + zoomy * (cmp[a].y - offsy); + const float fx = rect->xmin + zoomx * (cmp[a].x - offsx); + const float fy = rect->ymin + zoomy * (cmp[a].y - offsy); immAttr4fv(col, (cmp[a].flag & CUMA_SELECT) ? color_vert_select : color_vert); immVertex2f(pos, fx, fy); } @@ -2171,10 +2172,10 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, but_profile->edit_profile; /* Calculate offset and zoom. */ - float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect); - float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect); - float offsx = profile->view_rect.xmin - (1.0f / zoomx); - float offsy = profile->view_rect.ymin - (1.0f / zoomy); + const float zoomx = (BLI_rcti_size_x(rect) - 2.0f) / BLI_rctf_size_x(&profile->view_rect); + const float zoomy = (BLI_rcti_size_y(rect) - 2.0f) / BLI_rctf_size_y(&profile->view_rect); + const float offsx = profile->view_rect.xmin - (1.0f / zoomx); + const float offsy = profile->view_rect.ymin - (1.0f / zoomy); /* Exit early if too narrow. */ if (zoomx == 0.0f) { @@ -2190,7 +2191,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, .xmax = rect->xmax, .ymax = rect->ymax, }; - rcti scissor_region = {0, region->winx, 0, region->winy}; + const rcti scissor_region = {0, region->winx, 0, region->winy}; BLI_rcti_isect(&scissor_new, &scissor_region, &scissor_new); GPU_scissor(scissor_new.xmin, scissor_new.ymin, @@ -2235,10 +2236,10 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, } CurveProfilePoint *pts = profile->table; /* Also add the last points on the right and bottom edges to close off the fill polygon. */ - bool add_left_tri = profile->view_rect.xmin < 0.0f; - bool add_bottom_tri = profile->view_rect.ymin < 0.0f; + const bool add_left_tri = profile->view_rect.xmin < 0.0f; + const bool add_bottom_tri = profile->view_rect.ymin < 0.0f; uint tot_points = (uint)PROF_TABLE_LEN(profile->path_len) + 1 + add_left_tri + add_bottom_tri; - uint tot_triangles = tot_points - 2; + const uint tot_triangles = tot_points - 2; /* Create array of the positions of the table's points. */ float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords"); @@ -2349,7 +2350,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* New GPU instructions for control points and sampled points. */ format = immVertexFormat(); pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); /* Calculate vertex colors based on text theme. */ @@ -2448,8 +2449,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), .ymax = (float)recti->ymax - 1, }; - int width = BLI_rctf_size_x(&rect) + 1; - int height = BLI_rctf_size_y(&rect); + const int width = BLI_rctf_size_x(&rect) + 1; + const int height = BLI_rctf_size_y(&rect); GPU_blend(GPU_BLEND_ALPHA); @@ -2535,8 +2536,8 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), GPU_scissor(rect.xmin, rect.ymin, BLI_rctf_size_x(&rect), BLI_rctf_size_y(&rect)); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); UI_GetThemeColor4fv(TH_SEL_MARKER, col_sel); @@ -2547,10 +2548,10 @@ void ui_draw_but_TRACKPREVIEW(ARegion *UNUSED(region), const float pos_sel[8] = {-10.0f, -7.0f, -4.0f, -1.0f, 2.0f, 5.0f, 8.0f, 11.0f}; for (int axe = 0; axe < 2; axe++) { for (int i = 0; i < 7; i++) { - float x1 = pos_sel[i] * (1 - axe); - float y1 = pos_sel[i] * axe; - float x2 = pos_sel[i + 1] * (1 - axe); - float y2 = pos_sel[i + 1] * axe; + const float x1 = pos_sel[i] * (1 - axe); + const float y1 = pos_sel[i] * axe; + const float x2 = pos_sel[i + 1] * (1 - axe); + const float y2 = pos_sel[i + 1] * axe; if (i % 2 == 1) { immAttr4fv(col, col_sel); @@ -2669,7 +2670,7 @@ void UI_draw_box_shadow(uchar alpha, float minx, float miny, float maxx, float m GPU_blend(GPU_BLEND_ALPHA); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uint color = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c index b757341ae13..76b361a9e68 100644 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ b/source/blender/editors/interface/interface_eyedropper_colorband.c @@ -109,7 +109,7 @@ static bool eyedropper_colorband_init(bContext *C, wmOperator *op) } if (!band) { - PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); + const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); if (ptr.data != NULL) { band = ptr.data; @@ -200,7 +200,7 @@ static void eyedropper_colorband_apply(bContext *C, wmOperator *op) { EyedropperColorband *eye = op->customdata; /* Always filter, avoids noise in resulting color-band. */ - bool filter_samples = true; + const bool filter_samples = true; BKE_colorband_init_from_table_rgba( eye->color_band, eye->color_buffer, eye->color_buffer_len, filter_samples); eye->is_set = true; @@ -339,7 +339,7 @@ static bool eyedropper_colorband_poll(bContext *C) if (but && but->type == UI_BTYPE_COLORBAND) { return true; } - PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); + const PointerRNA ptr = CTX_data_pointer_get_type(C, "color_ramp", &RNA_ColorRamp); if (ptr.data != NULL) { return true; } diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index a162cac1b02..dd55d2c364b 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -117,7 +117,7 @@ static int datadropper_init(bContext *C, wmOperator *op) * because this struct has very short lifetime. */ ddr->idcode_name = TIP_(BKE_idtype_idcode_to_name(ddr->idcode)); - PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop); + const PointerRNA ptr = RNA_property_pointer_get(&ddr->ptr, ddr->prop); ddr->init_id = ptr.owner_id; return true; diff --git a/source/blender/editors/interface/interface_eyedropper_driver.c b/source/blender/editors/interface/interface_eyedropper_driver.c index 0162e205c29..d13e47624ee 100644 --- a/source/blender/editors/interface/interface_eyedropper_driver.c +++ b/source/blender/editors/interface/interface_eyedropper_driver.c @@ -95,8 +95,8 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve DriverDropper *ddr = (DriverDropper *)op->customdata; uiBut *but = eyedropper_get_property_button_under_mouse(C, event); - short mapping_type = RNA_enum_get(op->ptr, "mapping_type"); - short flag = 0; + const short mapping_type = RNA_enum_get(op->ptr, "mapping_type"); + const short flag = 0; /* we can only add a driver if we know what RNA property it corresponds to */ if (but == NULL) { @@ -105,7 +105,7 @@ static void driverdropper_sample(bContext *C, wmOperator *op, const wmEvent *eve /* Get paths for src... */ PointerRNA *target_ptr = &but->rnapoin; PropertyRNA *target_prop = but->rnaprop; - int target_index = but->rnaindex; + const int target_index = but->rnaindex; char *target_path = RNA_path_from_ID_to_property(target_ptr, target_prop); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 77cd3e00f3c..bf88b3c0318 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -85,23 +85,45 @@ # include "wm_window.h" #endif -/* place the mouse at the scaled down location when un-grabbing */ +/* -------------------------------------------------------------------- */ +/** \name Feature Defines + * + * These defines allow developers to locally toggle functionality which + * may be useful for testing (especially conflicts in dragging). + * Ideally the code would be refactored to support this functionality in a less fragile way. + * Until then keep these defines. + * \{ */ + +/** Place the mouse at the scaled down location when un-grabbing. */ #define USE_CONT_MOUSE_CORRECT -/* support dragging toggle buttons */ +/** Support dragging toggle buttons. */ #define USE_DRAG_TOGGLE -/* support dragging multiple number buttons at once */ +/** Support dragging multiple number buttons at once. */ #define USE_DRAG_MULTINUM -/* allow dragging/editing all other selected items at once */ +/** Allow dragging/editing all other selected items at once. */ #define USE_ALLSELECT -/* so we can avoid very small mouse-moves from jumping away from keyboard navigation [#34936] */ +/** + * Check to avoid very small mouse-moves from jumping away from keyboard navigation, + * while larger mouse motion will override keyboard input, see: T34936. + */ #define USE_KEYNAV_LIMIT -/* drag popups by their header */ +/** Support dragging popups by their header. */ #define USE_DRAG_POPUP +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Local Defines + * \{ */ + +/** + * The buffer side used for password strings, where the password is stored internally, + * but not displayed. + */ #define UI_MAX_PASSWORD_STR 128 /** @@ -117,7 +139,12 @@ */ #define UI_DRAG_MAP_SOFT_RANGE_PIXEL_MAX 1000 -/* proto */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Local Prototypes + * \{ */ + static int ui_do_but_EXIT(bContext *C, uiBut *but, struct uiHandleButtonData *data, @@ -131,6 +158,8 @@ static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEve static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event); #endif +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Structs & Defines * \{ */ @@ -667,9 +696,7 @@ static ListBase UIAfterFuncs = {NULL, NULL}; static uiAfterFunc *ui_afterfunc_new(void) { - uiAfterFunc *after; - - after = MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc"); + uiAfterFunc *after = MEM_callocN(sizeof(uiAfterFunc), "uiAfterFunc"); BLI_addtail(&UIAfterFuncs, after); @@ -718,7 +745,6 @@ static bool ui_afterfunc_check(const uiBlock *block, const uiBut *but) static void ui_apply_but_func(bContext *C, uiBut *but) { - uiAfterFunc *after; uiBlock *block = but->block; /* these functions are postponed and only executed after all other @@ -726,7 +752,7 @@ static void ui_apply_but_func(bContext *C, uiBut *but) * with these functions removing the buttons we are working with */ if (ui_afterfunc_check(block, but)) { - after = ui_afterfunc_new(); + uiAfterFunc *after = ui_afterfunc_new(); if (but->func && ELEM(but, but->func_arg1, but->func_arg2)) { /* exception, this will crash due to removed button otherwise */ @@ -788,8 +814,6 @@ static void ui_apply_but_func(bContext *C, uiBut *but) /* typically call ui_apply_but_undo(), ui_apply_but_autokey() */ static void ui_apply_but_undo(uiBut *but) { - uiAfterFunc *after; - if (but->flag & UI_BUT_UNDO) { const char *str = NULL; bool skip_undo = false; @@ -842,7 +866,7 @@ static void ui_apply_but_undo(uiBut *but) } /* delayed, after all other funcs run, popups are closed, etc */ - after = ui_afterfunc_new(); + uiAfterFunc *after = ui_afterfunc_new(); BLI_strncpy(after->undostr, str, sizeof(after->undostr)); } } @@ -874,16 +898,12 @@ static void ui_apply_but_autokey(bContext *C, uiBut *but) static void ui_apply_but_funcs_after(bContext *C) { - uiAfterFunc after; - PointerRNA opptr; - ListBase funcs; - /* copy to avoid recursive calls */ - funcs = UIAfterFuncs; + ListBase funcs = UIAfterFuncs; BLI_listbase_clear(&UIAfterFuncs); LISTBASE_FOREACH_MUTABLE (uiAfterFunc *, afterf, &funcs) { - after = *afterf; /* copy to avoid memleak on exit() */ + uiAfterFunc after = *afterf; /* copy to avoid memleak on exit() */ BLI_freelinkN(&funcs, afterf); if (after.context) { @@ -894,6 +914,7 @@ static void ui_apply_but_funcs_after(bContext *C) popup_check(C, after.popup_op); } + PointerRNA opptr; if (after.opptr) { /* free in advance to avoid leak on exit */ opptr = *after.opptr; @@ -1142,12 +1163,10 @@ static void ui_apply_but_CURVEPROFILE(bContext *C, uiBut *but, uiHandleButtonDat /* small multi-but api */ static void ui_multibut_add(uiHandleButtonData *data, uiBut *but) { - uiButMultiState *mbut_state; - BLI_assert(but->flag & UI_BUT_DRAG_MULTI); BLI_assert(data->multi_data.has_mbuts); - mbut_state = MEM_callocN(sizeof(*mbut_state), __func__); + uiButMultiState *mbut_state = MEM_callocN(sizeof(*mbut_state), __func__); mbut_state->but = but; mbut_state->origvalue = ui_but_value_get(but); # ifdef USE_ALLSELECT @@ -1161,9 +1180,7 @@ static void ui_multibut_add(uiHandleButtonData *data, uiBut *but) static uiButMultiState *ui_multibut_lookup(uiHandleButtonData *data, const uiBut *but) { - LinkNode *l; - - for (l = data->multi_data.mbuts; l; l = l->next) { + for (LinkNode *l = data->multi_data.mbuts; l; l = l->next) { uiButMultiState *mbut_state; mbut_state = l->link; @@ -1458,7 +1475,7 @@ static bool ui_drag_toggle_set_xy_xy( /* execute the button */ if (ui_drag_toggle_but_is_supported(but)) { /* is it pressed? */ - int pushed_state_but = ui_drag_toggle_but_pushed_state(C, but); + const int pushed_state_but = ui_drag_toggle_but_pushed_state(C, but); if (pushed_state_but != pushed_state) { UI_but_execute(C, region, but); if (do_check) { @@ -1487,7 +1504,6 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const { ARegion *region = CTX_wm_region(C); bool do_draw = false; - int xy[2]; /** * Initialize Locking: @@ -1526,6 +1542,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const } /* done with axis locking */ + int xy[2]; xy[0] = (drag_info->xy_lock[0] == false) ? xy_input[0] : drag_info->xy_last[0]; xy[1] = (drag_info->xy_lock[1] == false) ? xy_input[1] : drag_info->xy_last[1]; @@ -1599,17 +1616,16 @@ static bool ui_but_is_drag_toggle(const uiBut *but) static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore *selctx_data) { - PointerRNA ptr, lptr, idptr; - PropertyRNA *prop, *lprop; + PointerRNA lptr, idptr; + PropertyRNA *lprop; bool success = false; - int index; char *path = NULL; ListBase lb = {NULL}; - ptr = but->rnapoin; - prop = but->rnaprop; - index = but->rnaindex; + PointerRNA ptr = but->rnapoin; + PropertyRNA *prop = but->rnaprop; + const int index = but->rnaindex; /* for now don't support whole colors */ if (index == -1) { @@ -1618,9 +1634,7 @@ static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore /* if there is a valid property that is editable... */ if (ptr.data && prop) { - CollectionPointerLink *link; bool use_path_from_id; - int i; /* some facts we want to know */ const bool is_array = RNA_property_array_check(prop); @@ -1631,8 +1645,11 @@ static bool ui_selectcontext_begin(bContext *C, uiBut *but, uiSelectContextStore selctx_data->elems_len = BLI_listbase_count(&lb); selctx_data->elems = MEM_mallocN(sizeof(uiSelectContextElem) * selctx_data->elems_len, __func__); - - for (i = 0, link = lb.first; i < selctx_data->elems_len; i++, link = link->next) { + int i; + LISTBASE_FOREACH_INDEX (CollectionPointerLink *, link, &lb, i) { + if (i >= selctx_data->elems_len) { + break; + } uiSelectContextElem *other = &selctx_data->elems[i]; /* TODO,. de-duplicate copy_to_selected_button */ if (link->ptr.data != ptr.data) { @@ -1736,8 +1753,7 @@ static void ui_selectcontext_apply(bContext *C, if (selctx_data->elems) { PropertyRNA *prop = but->rnaprop; PropertyRNA *lprop = but->rnaprop; - int index = but->rnaindex; - int i; + const int index = but->rnaindex; const bool use_delta = (selctx_data->is_copy == false); union { @@ -1780,7 +1796,7 @@ static void ui_selectcontext_apply(bContext *C, # ifdef USE_ALLSELECT_LAYER_HACK /* make up for not having 'handle_layer_buttons' */ { - PropertySubType subtype = RNA_property_subtype(prop); + const PropertySubType subtype = RNA_property_subtype(prop); if ((rna_type == PROP_BOOLEAN) && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER) && is_array && /* could check for 'handle_layer_buttons' */ @@ -1792,7 +1808,7 @@ static void ui_selectcontext_apply(bContext *C, tmparray[index] = true; - for (i = 0; i < selctx_data->elems_len; i++) { + for (int i = 0; i < selctx_data->elems_len; i++) { uiSelectContextElem *other = &selctx_data->elems[i]; PointerRNA lptr = other->ptr; RNA_property_boolean_set_array(&lptr, lprop, tmparray); @@ -1807,7 +1823,7 @@ static void ui_selectcontext_apply(bContext *C, } # endif - for (i = 0; i < selctx_data->elems_len; i++) { + for (int i = 0; i < selctx_data->elems_len; i++) { uiSelectContextElem *other = &selctx_data->elems[i]; PointerRNA lptr = other->ptr; @@ -2016,14 +2032,7 @@ static void ui_apply_but_TRACKPREVIEW(bContext *C, uiBut *but, uiHandleButtonDat static void ui_apply_but( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const bool interactive) { - const int but_type = but->type; /* Store as const to quiet maybe uninitialized warning. */ - - char *editstr; - double *editval; - float *editvec; - ColorBand *editcoba; - CurveMapping *editcumap; - CurveProfile *editprofile; + const eButType but_type = but->type; /* Store as const to quiet maybe uninitialized warning. */ data->retval = 0; @@ -2076,9 +2085,12 @@ static void ui_apply_but( } /* ensures we are writing actual values */ - editstr = but->editstr; - editval = but->editval; - editvec = but->editvec; + char *editstr = but->editstr; + double *editval = but->editval; + float *editvec = but->editvec; + ColorBand *editcoba; + CurveMapping *editcumap; + CurveProfile *editprofile; if (but_type == UI_BTYPE_COLORBAND) { uiButColorBand *but_coba = (uiButColorBand *)but; editcoba = but_coba->edit_coba; @@ -2268,10 +2280,9 @@ static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleB static void ui_but_get_pasted_text_from_clipboard(char **buf_paste, int *buf_len) { - char *text; - int length; /* get only first line even if the clipboard contains multiple lines */ - text = WM_clipboard_text_get_firstline(false, &length); + int length; + char *text = WM_clipboard_text_get_firstline(false, &length); if (text) { *buf_paste = text; @@ -2342,7 +2353,7 @@ static void float_array_to_string(float *values, static void ui_but_copy_numeric_array(uiBut *but, char *output, int output_len_max) { - int array_length = get_but_property_array_length(but); + const int array_length = get_but_property_array_length(but); float *values = alloca(array_length * sizeof(float)); RNA_property_float_get_array(&but->rnapoin, but->rnaprop, values); float_array_to_string(values, array_length, output, output_len_max); @@ -2354,7 +2365,8 @@ static bool parse_float_array(char *text, float *values, int expected_length) BLI_assert(0 <= expected_length && expected_length <= 4); float v[5]; - int actual_length = sscanf(text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]); + const int actual_length = sscanf( + text, "[%f, %f, %f, %f, %f]", &v[0], &v[1], &v[2], &v[3], &v[4]); if (actual_length == expected_length) { memcpy(values, v, sizeof(float) * expected_length); @@ -2368,7 +2380,7 @@ static void ui_but_paste_numeric_array(bContext *C, uiHandleButtonData *data, char *buf_paste) { - int array_length = get_but_property_array_length(but); + const int array_length = get_but_property_array_length(but); if (array_length > 4) { // not supported for now return; @@ -2398,7 +2410,6 @@ static void ui_but_paste_numeric_value(bContext *C, char *buf_paste) { double value; - if (ui_but_string_eval_number(C, but, buf_paste, &value)) { button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); data->value = value; @@ -2460,7 +2471,7 @@ static void ui_but_paste_color(bContext *C, uiBut *but, char *buf_paste) } /* Some color properties are RGB, not RGBA. */ - int array_len = get_but_property_array_length(but); + const int array_len = get_but_property_array_length(but); BLI_assert(ELEM(array_len, 3, 4)); ui_but_set_float_array(C, but, NULL, rgba, array_len); } @@ -2556,8 +2567,7 @@ static void ui_but_paste_CurveProfile(bContext *C, uiBut *but) static void ui_but_copy_operator(bContext *C, uiBut *but, char *output, int output_len_max) { - PointerRNA *opptr; - opptr = UI_but_operator_ptr_get(but); + PointerRNA *opptr = UI_but_operator_ptr_get(but); char *str; str = WM_operator_pystring_ex(C, NULL, false, true, but->optype, opptr); @@ -2598,7 +2608,7 @@ static void ui_but_copy(bContext *C, uiBut *but, const bool copy_array) /* Left false for copying internal data (color-band for eg). */ bool is_buf_set = false; - bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL); + const bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL); switch (but->type) { case UI_BTYPE_NUM: @@ -2689,7 +2699,7 @@ static void ui_but_paste(bContext *C, uiBut *but, uiHandleButtonData *data, cons char *buf_paste; ui_but_get_pasted_text_from_clipboard(&buf_paste, &buf_paste_len); - bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL); + const bool has_required_data = !(but->poin == NULL && but->rnapoin.data == NULL); switch (but->type) { case UI_BTYPE_NUM: @@ -2769,12 +2779,9 @@ void ui_but_clipboard_free(void) static int ui_text_position_from_hidden(uiBut *but, int pos) { - const char *strpos, *butstr; - int i; - - butstr = (but->editstr) ? but->editstr : but->drawstr; - - for (i = 0, strpos = butstr; i < pos; i++) { + const char *butstr = (but->editstr) ? but->editstr : but->drawstr; + const char *strpos = butstr; + for (int i = 0; i < pos; i++) { strpos = BLI_str_find_next_char_utf8(strpos, NULL); } @@ -2791,13 +2798,11 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], uiBut *but, const bool restore) { - char *butstr; - if (!(but->rnaprop && RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) { return; } - butstr = (but->editstr) ? but->editstr : but->drawstr; + char *butstr = (but->editstr) ? but->editstr : but->drawstr; if (restore) { /* restore original string */ @@ -2898,7 +2903,7 @@ static bool ui_textedit_set_cursor_pos_foreach_glyph(const char *UNUSED(str), void *user_data) { int *cursor_data = user_data; - float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f); + const float center = glyph_step_bounds->xmin + (BLI_rctf_size_x(glyph_bounds) / 2.0f); if (cursor_data[0] < center) { cursor_data[1] = str_step_ofs; return false; @@ -2967,7 +2972,7 @@ static void ui_textedit_set_cursor_pos(uiBut *but, uiHandleButtonData *data, con else { str_last = &str[but->ofs]; const int str_last_len = strlen(str_last); - int x_pos = (int)(x - startx); + const int x_pos = (int)(x - startx); int glyph_data[2] = { x_pos, /* horizontal position to test. */ -1, /* Write the character offset here. */ @@ -3015,7 +3020,7 @@ static bool ui_textedit_insert_buf(uiBut *but, int buf_len) { int len = strlen(data->str); - int len_new = len - (but->selend - but->selsta) + 1; + const int len_new = len - (but->selend - but->selsta) + 1; bool changed = false; if (data->is_str_dynamic) { @@ -3167,11 +3172,9 @@ static bool ui_textedit_delete(uiBut *but, static int ui_textedit_autocomplete(bContext *C, uiBut *but, uiHandleButtonData *data) { - char *str; - int changed; - - str = data->str; + char *str = data->str; + int changed; if (data->searchbox) { changed = ui_searchbox_autocomplete(C, data->searchbox, but, data->str); } @@ -3194,14 +3197,13 @@ enum { static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const int mode) { - char *pbuf; bool changed = false; - int buf_len; /* paste */ if (mode == UI_TEXTEDIT_PASTE) { /* extract the first line from the clipboard */ - pbuf = WM_clipboard_text_get_firstline(false, &buf_len); + int buf_len; + char *pbuf = WM_clipboard_text_get_firstline(false, &buf_len); if (pbuf) { if (UI_but_is_utf8(but)) { @@ -3218,7 +3220,7 @@ static bool ui_textedit_copypaste(uiBut *but, uiHandleButtonData *data, const in /* cut & copy */ else if (ELEM(mode, UI_TEXTEDIT_COPY, UI_TEXTEDIT_CUT)) { /* copy the contents to the copypaste buffer */ - int sellen = but->selend - but->selsta; + const int sellen = but->selend - but->selsta; char *buf = MEM_mallocN(sizeof(char) * (sellen + 1), "ui_textedit_copypaste"); BLI_strncpy(buf, data->str + but->selsta, sellen + 1); @@ -3281,7 +3283,6 @@ wmIMEData *ui_but_ime_data_get(uiBut *but) static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) { wmWindow *win = data->window; - int len; const bool is_num_but = ELEM(but->type, UI_BTYPE_NUM, UI_BTYPE_NUM_SLIDER); bool no_zero_strip = false; @@ -3336,7 +3337,7 @@ static void ui_textedit_begin(bContext *C, uiBut *but, uiHandleButtonData *data) } /* won't change from now on */ - len = strlen(data->str); + const int len = strlen(data->str); data->origstr = BLI_strdupn(data->str, len); data->sel_pos_init = 0; @@ -3379,7 +3380,7 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) if (but) { if (UI_but_is_utf8(but)) { - int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr)); + const int strip = BLI_utf8_invalid_strip(but->editstr, strlen(but->editstr)); /* not a file?, strip non utf-8 chars */ if (strip) { /* wont happen often so isn't that annoying to keep it here for a while */ @@ -3424,8 +3425,6 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data) { - uiBut *but; - /* label and roundbox can overlap real buttons (backdrops...) */ if (ELEM(actbut->type, UI_BTYPE_LABEL, @@ -3436,7 +3435,7 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa return; } - for (but = actbut->next; but; but = but->next) { + for (uiBut *but = actbut->next; but; but = but->next) { if (ui_but_is_editable_as_text(but)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; @@ -3445,7 +3444,7 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } } } - for (but = block->buttons.first; but != actbut; but = but->next) { + for (uiBut *but = block->buttons.first; but != actbut; but = but->next) { if (ui_but_is_editable_as_text(but)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; @@ -3458,8 +3457,6 @@ static void ui_textedit_next_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonData *data) { - uiBut *but; - /* label and roundbox can overlap real buttons (backdrops...) */ if (ELEM(actbut->type, UI_BTYPE_LABEL, @@ -3470,7 +3467,7 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa return; } - for (but = actbut->prev; but; but = but->prev) { + for (uiBut *but = actbut->prev; but; but = but->prev) { if (ui_but_is_editable_as_text(but)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; @@ -3479,7 +3476,7 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa } } } - for (but = block->buttons.last; but != actbut; but = but->prev) { + for (uiBut *but = block->buttons.last; but != actbut; but = but->prev) { if (ui_but_is_editable_as_text(but)) { if (!(but->flag & UI_BUT_DISABLED)) { data->postbut = but; @@ -3499,9 +3496,9 @@ static void ui_do_but_textedit( #ifdef WITH_INPUT_IME wmWindow *win = CTX_wm_window(C); wmIMEData *ime_data = win->ime_data; - bool is_ime_composing = ime_data && ime_data->is_ime_composing; + const bool is_ime_composing = ime_data && ime_data->is_ime_composing; #else - bool is_ime_composing = false; + const bool is_ime_composing = false; #endif switch (event->type) { @@ -3517,7 +3514,7 @@ static void ui_do_but_textedit( ui_searchbox_event(C, data->searchbox, but, data->region, event); } #else - ui_searchbox_event(C, data->searchbox, but, event); + ui_searchbox_event(C, data->searchbox, but, data->region, event); #endif } @@ -3548,7 +3545,7 @@ static void ui_do_but_textedit( } break; case LEFTMOUSE: { - bool had_selection = but->selsta != but->selend; + const bool had_selection = but->selsta != but->selend; /* exit on LMB only on RELEASE for searchbox, to mimic other popups, * and allow multiple menu levels */ @@ -3559,10 +3556,8 @@ static void ui_do_but_textedit( /* for double click: we do a press again for when you first click on button * (selects all text, no cursor pos) */ if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) { - float mx, my; - - mx = event->x; - my = event->y; + float mx = event->x; + float my = event->y; ui_window_to_block_fl(data->region, block, &mx, &my); if (ui_but_contains_pt(but, mx, my)) { @@ -3708,7 +3703,7 @@ static void ui_do_but_textedit( case EVT_TABKEY: /* there is a key conflict here, we can't tab with autocomplete */ if (but->autocomplete_func || data->searchbox) { - int autocomplete = ui_textedit_autocomplete(C, but, data); + const int autocomplete = ui_textedit_autocomplete(C, but, data); changed = autocomplete != AUTOCOMPLETE_NO_MATCH; if (autocomplete == AUTOCOMPLETE_FULL_MATCH) { @@ -3772,7 +3767,7 @@ static void ui_do_but_textedit( } if (utf8_buf && utf8_buf[0]) { - int utf8_buf_len = BLI_str_utf8_size(utf8_buf); + const int utf8_buf_len = BLI_str_utf8_size(utf8_buf); BLI_assert(utf8_buf_len != -1); changed = ui_textedit_insert_buf(but, data, event->utf8_buf, utf8_buf_len); } @@ -3832,12 +3827,12 @@ static void ui_do_but_textedit( static void ui_do_but_textedit_select( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my, retval = WM_UI_HANDLER_CONTINUE; + int retval = WM_UI_HANDLER_CONTINUE; switch (event->type) { case MOUSEMOVE: { - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); ui_textedit_set_cursor_select(but, data, event->x); @@ -4297,7 +4292,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C, if (event->type == LEFTMOUSE && event->val == KM_PRESS) { /* only cancel if click outside the button */ - if (ui_but_contains_point_px(but, but->active->region, event->x, event->y) == 0) { + if (ui_but_contains_point_px(but, but->active->region, event->x, event->y) == false) { /* data->cancel doesn't work, this button opens immediate */ if (but->flag & UI_BUT_IMMEDIATE) { ui_but_value_set(but, 0); @@ -4407,7 +4402,7 @@ static int ui_do_but_TAB( return WM_UI_HANDLER_BREAK; } if (ELEM(event->type, LEFTMOUSE, EVT_PADENTER, EVT_RETKEY)) { - int event_val = (is_property) ? KM_PRESS : KM_CLICK; + const int event_val = (is_property) ? KM_PRESS : KM_CLICK; if (event->val == event_val) { button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; @@ -4546,7 +4541,6 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - if (data->state == BUTTON_STATE_HIGHLIGHT) { /* first handle click on icondrag type button */ @@ -4616,7 +4610,7 @@ static float ui_numedit_apply_snapf( if (ui_but_is_unit(but)) { UnitSettings *unit = but->block->unit; - int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); + const int unit_type = RNA_SUBTYPE_UNIT_VALUE(UI_but_unit_type_get(but)); if (bUnit_IsValid(unit->system, unit_type)) { fac = (float)bUnit_BaseScalar(unit->system, unit_type); @@ -4639,7 +4633,7 @@ static float ui_numedit_apply_snapf( * but allow for rotations */ if (softrange >= 21.0f) { UnitSettings *unit = but->block->unit; - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); if ((unit_type == PROP_UNIT_ROTATION) && (unit->system_rotation != USER_UNIT_ROT_RADIANS)) { /* pass (degrees)*/ } @@ -4883,7 +4877,7 @@ static bool ui_numedit_but_NUM(uiBut *but, static void ui_numedit_set_active(uiBut *but) { - int oldflag = but->drawflag; + const int oldflag = but->drawflag; but->drawflag &= ~(UI_BUT_ACTIVE_LEFT | UI_BUT_ACTIVE_RIGHT); uiHandleButtonData *data = but->active; @@ -4933,13 +4927,14 @@ static void ui_numedit_set_active(uiBut *but) static int ui_do_but_NUM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; /* mouse location scaled to fit the UI */ - int screen_mx, screen_my; /* mouse location kept at screen pixel coords */ int click = 0; int retval = WM_UI_HANDLER_CONTINUE; - mx = screen_mx = event->x; - my = screen_my = event->y; + /* mouse location scaled to fit the UI */ + int mx = event->x; + int my = event->y; + /* mouse location kept at screen pixel coords */ + const int screen_mx = event->x; ui_window_to_block(data->region, block, &mx, &my); ui_numedit_set_active(but); @@ -5154,7 +5149,7 @@ static bool ui_numedit_but_SLI(uiBut *but, (but->softmax - but->softmin + but->a1); } else { - float offs = (BLI_rctf_size_y(&but->rect) / 2.0f); + const float offs = (BLI_rctf_size_y(&but->rect) / 2.0f); cursor_x_range = (BLI_rctf_size_x(&but->rect) - offs); } @@ -5245,11 +5240,11 @@ static bool ui_numedit_but_SLI(uiBut *but, static int ui_do_but_SLI( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my, click = 0; + int click = 0; int retval = WM_UI_HANDLER_CONTINUE; - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5448,12 +5443,11 @@ static int ui_do_but_SLI( static int ui_do_but_SCROLL( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my /*, click = 0 */; int retval = WM_UI_HANDLER_CONTINUE; - bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect)); + const bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect)); - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5470,12 +5464,6 @@ static int ui_do_but_SCROLL( button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); retval = WM_UI_HANDLER_BREAK; } - /* UNUSED - otherwise code is ok, add back if needed */ -#if 0 - else if (ELEM(event->type, PADENTER, RETKEY) && event->val == KM_PRESS) { - click = 1; - } -#endif } } else if (data->state == BUTTON_STATE_NUM_EDITING) { @@ -5506,7 +5494,6 @@ static int ui_do_but_SCROLL( static int ui_do_but_GRIP( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; int retval = WM_UI_HANDLER_CONTINUE; const bool horizontal = (BLI_rctf_size_x(&but->rect) < BLI_rctf_size_y(&but->rect)); @@ -5516,8 +5503,8 @@ static int ui_do_but_GRIP( * See T37739. */ - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5581,7 +5568,6 @@ static int ui_do_but_LISTROW(bContext *C, static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - if (data->state == BUTTON_STATE_HIGHLIGHT) { /* first handle click on icondrag type button */ @@ -5666,8 +5652,7 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co static bool ui_numedit_but_UNITVEC( uiBut *but, uiHandleButtonData *data, int mx, int my, const enum eSnapType snap) { - float dx, dy, rad, radsq, mrad, *fp; - int mdx, mdy; + float mrad; bool changed = true; /* button is presumed square */ @@ -5676,10 +5661,11 @@ static bool ui_numedit_but_UNITVEC( /* note that both data->vec and data->origvec should be normalized * else we'll get a harmless but annoying jump when first clicking */ - fp = data->origvec; - rad = BLI_rctf_size_x(&but->rect); - radsq = rad * rad; + float *fp = data->origvec; + const float rad = BLI_rctf_size_x(&but->rect); + const float radsq = rad * rad; + int mdx, mdy; if (fp[2] > 0.0f) { mdx = (rad * fp[0]); mdy = (rad * fp[1]); @@ -5694,8 +5680,8 @@ static bool ui_numedit_but_UNITVEC( mdx = mdy = 0; } - dx = (float)(mx + mdx - data->dragstartx); - dy = (float)(my + mdy - data->dragstarty); + float dx = (float)(mx + mdx - data->dragstartx); + float dy = (float)(my + mdy - data->dragstarty); fp = data->vec; mrad = dx * dx + dy * dy; @@ -5724,11 +5710,10 @@ static bool ui_numedit_but_UNITVEC( const int snap_steps = (snap == SNAP_ON) ? 4 : 12; /* 45 or 15 degree increments */ const float snap_steps_angle = M_PI / snap_steps; float angle, angle_snap; - int i; /* round each axis of 'fp' to the next increment * do this in "angle" space - this gives increments of same size */ - for (i = 0; i < 3; i++) { + for (int i = 0; i < 3; i++) { angle = asinf(fp[i]); angle_snap = roundf((angle / snap_steps_angle)) * snap_steps_angle; fp[i] = sinf(angle_snap); @@ -5799,7 +5784,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co hsv[2] = clamp_f(hsv[2] + 0.05f, 0.0f, 1.0f); } else { - float fac = 0.005 * (event->y - event->prevy); + const float fac = 0.005 * (event->y - event->prevy); hsv[2] = clamp_f(hsv[2] + fac, 0.0f, 1.0f); } @@ -5903,10 +5888,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co static int ui_do_but_UNITVEC( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6020,7 +6003,7 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but, float rgb[3]; float x, y; float mx_fl, my_fl; - bool changed = true; + const bool changed = true; ui_mouse_scale_warp(data, mx, my, &mx_fl, &my_fl, shift); @@ -6093,7 +6076,7 @@ static bool ui_numedit_but_HSVCUBE(uiBut *but, break; case UI_GRAD_V_ALT: { /* vertical 'value' strip */ - float min = but->softmin, max = but->softmax; + const float min = but->softmin, max = but->softmax; /* exception only for value strip - use the range set in but->min/max */ hsv[2] = y * (max - min) + min; break; @@ -6136,7 +6119,7 @@ static void ui_ndofedit_but_HSVCUBE(uiButHSVCube *hsv_but, float *hsv = cpicker->color_data; const float hsv_v_max = max_ff(hsv[2], hsv_but->but.softmax); float rgb[3]; - float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt; + const float sensitivity = (shift ? 0.15f : 0.3f) * ndof->dt; ui_but_v3_get(&hsv_but->but, rgb); ui_scene_linear_to_color_picker_space(&hsv_but->but, rgb); @@ -6199,10 +6182,8 @@ static int ui_do_but_HSVCUBE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { uiButHSVCube *hsv_but = (uiButHSVCube *)but; - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6301,13 +6282,11 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but, const enum eSnapType snap, const bool shift) { - rcti rect; - bool changed = true; - float mx_fl, my_fl; - float rgb[3]; + const bool changed = true; ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->color_data; + float mx_fl, my_fl; ui_mouse_scale_warp(data, mx, my, &mx_fl, &my_fl, shift); #ifdef USE_CONT_MOUSE_CORRECT @@ -6326,8 +6305,10 @@ static bool ui_numedit_but_HSVCIRCLE(uiBut *but, } #endif + rcti rect; BLI_rcti_rctf_copy(&rect, &but->rect); + float rgb[3]; ui_but_v3_get(but, rgb); ui_scene_linear_to_color_picker_space(but, rgb); ui_rgb_to_color_picker_compat_v(rgb, hsv); @@ -6405,7 +6386,7 @@ static void ui_ndofedit_but_HSVCIRCLE(uiBut *but, float *hsv = cpicker->color_data; float rgb[3]; float phi, r /*, sqr */ /* UNUSED */, v[2]; - float sensitivity = (shift ? 0.06f : 0.3f) * ndof->dt; + const float sensitivity = (shift ? 0.06f : 0.3f) * ndof->dt; ui_but_v3_get(but, rgb); ui_scene_linear_to_color_picker_space(but, rgb); @@ -6477,9 +6458,8 @@ static int ui_do_but_HSVCIRCLE( { ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->color_data; - int mx, my; - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6581,7 +6561,6 @@ static int ui_do_but_HSVCIRCLE( static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int mx) { - float dx; bool changed = false; if (data->draglastx == mx) { @@ -6592,7 +6571,7 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m return changed; } - dx = ((float)(mx - data->draglastx)) / BLI_rctf_size_x(&but->rect); + const float dx = ((float)(mx - data->draglastx)) / BLI_rctf_size_x(&but->rect); data->dragcbd->pos += dx; CLAMP(data->dragcbd->pos, 0.0f, 1.0f); @@ -6608,33 +6587,32 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m static int ui_do_but_COLORBAND( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - ColorBand *coba; - CBData *cbd; - /* ignore zoom-level for mindist */ - int mindist = (50 * UI_DPI_FAC) * block->aspect; - int mx, my, a, xco; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS) { - coba = (ColorBand *)but->poin; + ColorBand *coba = (ColorBand *)but->poin; if (event->ctrl) { /* insert new key on mouse location */ - float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect); + const float pos = ((float)(mx - but->rect.xmin)) / BLI_rctf_size_x(&but->rect); BKE_colorband_element_add(coba, pos); button_activate_state(C, but, BUTTON_STATE_EXIT); } else { + CBData *cbd; + /* ignore zoom-level for mindist */ + int mindist = (50 * UI_DPI_FAC) * block->aspect; + int xco; data->dragstartx = mx; data->dragstarty = my; data->draglastx = mx; data->draglasty = my; /* activate new key when mouse is close */ + int a; for (a = 0, cbd = coba->data; a < coba->tot; a++, cbd++) { xco = but->rect.xmin + (cbd->pos * BLI_rctf_size_x(&but->rect)); xco = abs(xco - mx); @@ -6693,22 +6671,19 @@ static bool ui_numedit_but_CURVE(uiBlock *block, CurveMapping *cumap = (CurveMapping *)but->poin; CurveMap *cuma = cumap->cm + cumap->cur; CurveMapPoint *cmp = cuma->curve; - float fx, fy, zoomx, zoomy; - int mx, my, dragx, dragy; - int a; bool changed = false; /* evtx evty and drag coords are absolute mousecoords, * prevents errors when editing when layout changes */ - mx = evtx; - my = evty; + int mx = evtx; + int my = evty; ui_window_to_block(data->region, block, &mx, &my); - dragx = data->draglastx; - dragy = data->draglasty; + int dragx = data->draglastx; + int dragy = data->draglasty; ui_window_to_block(data->region, block, &dragx, &dragy); - zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&cumap->curr); - zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&cumap->curr); + const float zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&cumap->curr); + const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&cumap->curr); if (snap) { float d[2]; @@ -6721,20 +6696,20 @@ static bool ui_numedit_but_CURVE(uiBlock *block, } } + float fx = (mx - dragx) / zoomx; + float fy = (my - dragy) / zoomy; + if (data->dragsel != -1) { CurveMapPoint *cmp_last = NULL; const float mval_factor = ui_mouse_scale_warp_factor(shift); bool moved_point = false; /* for ctrl grid, can't use orig coords because of sorting */ - fx = (mx - dragx) / zoomx; - fy = (my - dragy) / zoomy; - fx *= mval_factor; fy *= mval_factor; - for (a = 0; a < cuma->totpoint; a++) { + for (int a = 0; a < cuma->totpoint; a++) { if (cmp[a].flag & CUMA_SELECT) { - float origx = cmp[a].x, origy = cmp[a].y; + const float origx = cmp[a].x, origy = cmp[a].y; cmp[a].x += fx; cmp[a].y += fy; if (snap) { @@ -6771,9 +6746,6 @@ static bool ui_numedit_but_CURVE(uiBlock *block, data->dragchange = true; /* mark for selection */ } else { - fx = (mx - dragx) / zoomx; - fy = (my - dragy) / zoomy; - /* clamp for clip */ if (cumap->flag & CUMA_DO_CLIP) { if (cumap->curr.xmin - fx < cumap->clipr.xmin) { @@ -6807,20 +6779,18 @@ static bool ui_numedit_but_CURVE(uiBlock *block, static int ui_do_but_CURVE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my, a; bool changed = false; Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS) { CurveMapping *cumap = (CurveMapping *)but->poin; CurveMap *cuma = cumap->cm + cumap->cur; - CurveMapPoint *cmp; const float m_xy[2] = {mx, my}; float dist_min_sq = square_f(U.dpi_fac * 14.0f); /* 14 pixels radius */ int sel = -1; @@ -6835,8 +6805,8 @@ static int ui_do_but_CURVE( } /* check for selecting of a point */ - cmp = cuma->curve; /* ctrl adds point, new malloc */ - for (a = 0; a < cuma->totpoint; a++) { + CurveMapPoint *cmp = cuma->curve; /* ctrl adds point, new malloc */ + for (int a = 0; a < cuma->totpoint; a++) { float f_xy[2]; BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[a].x); const float dist_sq = len_squared_v2v2(m_xy, f_xy); @@ -6847,7 +6817,6 @@ static int ui_do_but_CURVE( } if (sel == -1) { - int i; float f_xy[2], f_xy_prev[2]; /* if the click didn't select anything, check if it's clicked on the @@ -6860,7 +6829,7 @@ static int ui_do_but_CURVE( dist_min_sq = square_f(U.dpi_fac * 8.0f); /* loop through the curve segment table and find what's near the mouse. */ - for (i = 1; i <= CM_TABLE; i++) { + for (int i = 1; i <= CM_TABLE; i++) { copy_v2_v2(f_xy_prev, f_xy); BLI_rctf_transform_pt_v(&but->rect, &cumap->curr, f_xy, &cmp[i].x); @@ -6877,7 +6846,7 @@ static int ui_do_but_CURVE( cmp = cuma->curve; /* find newly added point and make it 'sel' */ - for (a = 0; a < cuma->totpoint; a++) { + for (int a = 0; a < cuma->totpoint; a++) { if (cmp[a].x == f_xy[0]) { sel = a; } @@ -6891,7 +6860,7 @@ static int ui_do_but_CURVE( /* ok, we move a point */ /* deselect all if this one is deselect. except if we hold shift */ if (!event->shift) { - for (a = 0; a < cuma->totpoint; a++) { + for (int a = 0; a < cuma->totpoint; a++) { cmp[a].flag &= ~CUMA_SELECT; } cmp[sel].flag |= CUMA_SELECT; @@ -6935,7 +6904,7 @@ static int ui_do_but_CURVE( if (data->dragchange == false) { /* deselect all, select one */ if (!event->shift) { - for (a = 0; a < cuma->totpoint; a++) { + for (int a = 0; a < cuma->totpoint; a++) { cmp[a].flag &= ~CUMA_SELECT; } cmp[data->dragsel].flag |= CUMA_SELECT; @@ -6970,22 +6939,19 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, { CurveProfile *profile = (CurveProfile *)but->poin; CurveProfilePoint *pts = profile->path; - float fx, fy, zoomx, zoomy; - int mx, my, dragx, dragy; - int a; bool changed = false; /* evtx evty and drag coords are absolute mousecoords, * prevents errors when editing when layout changes */ - mx = evtx; - my = evty; + int mx = evtx; + int my = evty; ui_window_to_block(data->region, block, &mx, &my); - dragx = data->draglastx; - dragy = data->draglasty; + int dragx = data->draglastx; + int dragy = data->draglasty; ui_window_to_block(data->region, block, &dragx, &dragy); - zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect); - zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect); + const float zoomx = BLI_rctf_size_x(&but->rect) / BLI_rctf_size_x(&profile->view_rect); + const float zoomy = BLI_rctf_size_y(&but->rect) / BLI_rctf_size_y(&profile->view_rect); if (snap) { float d[2]; @@ -6998,8 +6964,8 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, } } - fx = (mx - dragx) / zoomx; - fy = (my - dragy) / zoomy; + float fx = (mx - dragx) / zoomx; + float fy = (my - dragy) / zoomy; if (data->dragsel != -1) { float last_x, last_y; @@ -7011,7 +6977,7 @@ static bool ui_numedit_but_CURVEPROFILE(uiBlock *block, /* Move all selected points. */ const float delta[2] = {fx, fy}; - for (a = 0; a < profile->path_len; a++) { + for (int a = 0; a < profile->path_len; a++) { /* Don't move the last and first control points. */ if ((pts[a].flag & PROF_SELECT) && (a != 0) && (a != profile->path_len)) { moved_point |= BKE_curveprofile_move_point(profile, &pts[a], snap, delta); @@ -7265,8 +7231,8 @@ static int ui_do_but_CURVEPROFILE( static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int mx, int my) { Histogram *hist = (Histogram *)but->poin; - bool changed = true; - float dy = my - data->draglasty; + const bool changed = true; + const float dy = my - data->draglasty; /* scale histogram values (dy / 10 for better control) */ const float yfac = min_ff(pow2f(hist->ymax), 1.0f) * 0.5f; @@ -7284,10 +7250,8 @@ static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int m static int ui_do_but_HISTOGRAM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7341,10 +7305,9 @@ static int ui_do_but_HISTOGRAM( static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx, int my) { Scopes *scopes = (Scopes *)but->poin; - bool changed = true; - float dy; + const bool changed = true; - dy = my - data->draglasty; + const float dy = my - data->draglasty; /* scale waveform values */ scopes->wavefrm_yfac += dy / 200.0f; @@ -7360,10 +7323,8 @@ static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx static int ui_do_but_WAVEFORM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7418,11 +7379,10 @@ static bool ui_numedit_but_TRACKPREVIEW( bContext *C, uiBut *but, uiHandleButtonData *data, int mx, int my, const bool shift) { MovieClipScopes *scopes = (MovieClipScopes *)but->poin; - bool changed = true; - float dx, dy; + const bool changed = true; - dx = mx - data->draglastx; - dy = my - data->draglasty; + float dx = mx - data->draglastx; + float dy = my - data->draglasty; if (shift) { dx /= 5.0f; @@ -7431,7 +7391,7 @@ static bool ui_numedit_but_TRACKPREVIEW( if (!scopes->track_locked) { const MovieClip *clip = CTX_data_edit_movieclip(C); - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, scopes->framenr); + const int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip, scopes->framenr); if (scopes->marker->framenr != clip_framenr) { scopes->marker = BKE_tracking_marker_ensure(scopes->track, clip_framenr); } @@ -7454,10 +7414,8 @@ static bool ui_numedit_but_TRACKPREVIEW( static int ui_do_but_TRACKPREVIEW( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7502,13 +7460,10 @@ static int ui_do_but_TRACKPREVIEW( static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent *event) { - uiHandleButtonData *data; - int retval; - - data = but->active; - retval = WM_UI_HANDLER_CONTINUE; + uiHandleButtonData *data = but->active; + int retval = WM_UI_HANDLER_CONTINUE; - bool is_disabled = but->flag & UI_BUT_DISABLED; + const bool is_disabled = but->flag & UI_BUT_DISABLED; /* if but->pointype is set, but->poin should be too */ BLI_assert(!but->pointype || but->poin); @@ -7521,8 +7476,8 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* handle copy and paste */ bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) && !event->shift; - bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift; - bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift; + const bool do_copy = event->type == EVT_CKEY && is_press_ctrl_but_no_shift; + const bool do_paste = event->type == EVT_VKEY && is_press_ctrl_but_no_shift; /* Specific handling for listrows, we try to find their overlapping tex button. */ if ((do_copy || do_paste) && but->type == UI_BTYPE_LISTROW) { @@ -7798,9 +7753,7 @@ void UI_but_tooltip_refresh(bContext *C, uiBut *but) */ void UI_but_tooltip_timer_remove(bContext *C, uiBut *but) { - uiHandleButtonData *data; - - data = but->active; + uiHandleButtonData *data = but->active; if (data) { if (data->autoopentimer) { WM_event_remove_timer(data->wm, data->window, data->autoopentimer); @@ -7841,8 +7794,8 @@ static void button_tooltip_timer_reset(bContext *C, uiBut *but) if ((U.flag & USER_TOOLTIPS) || (data->tooltip_force)) { if (!but->block->tooltipdisabled) { if (!wm->drags.first) { - bool is_label = UI_but_has_tooltip_label(but); - double delay = is_label ? UI_TOOLTIP_DELAY_LABEL : UI_TOOLTIP_DELAY; + const bool is_label = UI_but_has_tooltip_label(but); + const double delay = is_label ? UI_TOOLTIP_DELAY_LABEL : UI_TOOLTIP_DELAY; WM_tooltip_timer_init_ex( C, data->window, data->area, data->region, ui_but_tooltip_init, delay); if (is_label) { @@ -7875,9 +7828,7 @@ static bool button_modal_state(uiHandleButtonState state) static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState state) { - uiHandleButtonData *data; - - data = but->active; + uiHandleButtonData *data = but->active; if (data->state == state) { return; } @@ -8047,13 +7998,11 @@ static void button_activate_init(bContext *C, uiBut *but, uiButtonActivateType type) { - uiHandleButtonData *data; - /* Only ever one active button! */ BLI_assert(ui_region_find_active_but(region) == NULL); /* setup struct */ - data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData"); + uiHandleButtonData *data = MEM_callocN(sizeof(uiHandleButtonData), "uiHandleButtonData"); data->wm = CTX_wm_manager(C); data->window = CTX_wm_window(C); data->area = CTX_wm_area(C); @@ -8277,13 +8226,11 @@ static void button_activate_exit( void ui_but_active_free(const bContext *C, uiBut *but) { - uiHandleButtonData *data; - /* this gets called when the button somehow disappears while it is still * active, this is bad for user interaction, but we need to handle this * case cleanly anyway in case it happens */ if (but->active) { - data = but->active; + uiHandleButtonData *data = but->active; data->cancel = true; button_activate_exit((bContext *)C, but, data, false, true); } @@ -8462,11 +8409,10 @@ void UI_context_update_anim_flag(const bContext *C) struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( depsgraph, (scene) ? scene->r.cfra : 0.0f); - uiBut *activebut; while (region) { /* find active button */ - activebut = NULL; + uiBut *activebut = NULL; LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { LISTBASE_FOREACH (uiBut *, but, &block->buttons) { @@ -8524,10 +8470,8 @@ static uiBut *ui_but_find_open_event(ARegion *region, const wmEvent *event) static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *region) { - uiBut *but; - if (event->type == MOUSEMOVE) { - but = ui_but_find_mouse_over(region, event); + uiBut *but = ui_but_find_mouse_over(region, event); if (but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); @@ -8538,7 +8482,7 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg } } else if (event->type == EVT_BUT_OPEN) { - but = ui_but_find_open_event(region, event); + uiBut *but = ui_but_find_open_event(region, event); if (but) { button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); ui_do_button(C, but->block, but, event); @@ -8556,10 +8500,10 @@ static int ui_handle_button_over(bContext *C, const wmEvent *event, ARegion *reg void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but) { wmWindow *win = CTX_wm_window(C); - wmEvent event; button_activate_init(C, region, but, BUTTON_ACTIVATE_OVER); + wmEvent event; wm_event_init_from_window(win, &event); event.type = EVT_BUT_OPEN; event.val = KM_PRESS; @@ -8617,12 +8561,9 @@ static void ui_handle_button_activate(bContext *C, uiBut *but, uiButtonActivateType type) { - uiBut *oldbut; - uiHandleButtonData *data; - - oldbut = ui_region_find_active_but(region); + uiBut *oldbut = ui_region_find_active_but(region); if (oldbut) { - data = oldbut->active; + uiHandleButtonData *data = oldbut->active; data->cancel = true; button_activate_exit(C, oldbut, data, false, false); } @@ -8665,7 +8606,7 @@ static bool ui_handle_button_activate_by_type(bContext *C, ARegion *region, uiBu static bool ui_button_value_default(uiBut *but, double *r_value) { if (but->rnaprop != NULL && ui_but_is_rna_valid(but)) { - int type = RNA_property_type(but->rnaprop); + const int type = RNA_property_type(but->rnaprop); if (ELEM(type, PROP_FLOAT, PROP_INT)) { double default_value; switch (type) { @@ -8699,14 +8640,11 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) { uiHandleButtonData *data = but->active; const uiHandleButtonState state_orig = data->state; - uiBlock *block; - ARegion *region; - int retval; - block = but->block; - region = data->region; + uiBlock *block = but->block; + ARegion *region = data->region; - retval = WM_UI_HANDLER_CONTINUE; + int retval = WM_UI_HANDLER_CONTINUE; if (data->state == BUTTON_STATE_HIGHLIGHT) { switch (event->type) { @@ -8918,7 +8856,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) data = but->active; if (data && data->state == BUTTON_STATE_EXIT) { uiBut *post_but = data->postbut; - uiButtonActivateType post_type = data->posttype; + const uiButtonActivateType post_type = data->posttype; /* Reset the button value when empty text is typed. */ if ((data->cancel == false) && (data->str != NULL) && (data->str[0] == '\0') && @@ -8973,21 +8911,18 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *region, uiBut *listbox) { - uiList *ui_list; - uiListDyn *dyn_data; int retval = WM_UI_HANDLER_CONTINUE; int type = event->type, val = event->val; bool redraw = false; - int mx, my; - ui_list = listbox->custom_data; + uiList *ui_list = listbox->custom_data; if (!ui_list || !ui_list->dyn_data) { return retval; } - dyn_data = ui_list->dyn_data; + uiListDyn *dyn_data = ui_list->dyn_data; - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(region, listbox->block, &mx, &my); /* Convert pan to scroll-wheel. */ @@ -9023,11 +8958,11 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi * collection order, we have some work! */ int *org_order = MEM_mallocN(dyn_data->items_shown * sizeof(int), __func__); const int *new_order = dyn_data->items_filter_neworder; - int i, org_idx = -1, len = dyn_data->items_len; + int org_idx = -1, len = dyn_data->items_len; int current_idx = -1; - int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; + const int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE; - for (i = 0; i < len; i++) { + for (int i = 0; i < len; i++) { if (!dyn_data->items_filter_flags || ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { org_order[new_order ? new_order[++org_idx] : ++org_idx] = i; @@ -9106,11 +9041,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but) { - uiHandleButtonData *data; - uiPopupBlockHandle *menu; - - data = but->active; - menu = data->menu; + uiHandleButtonData *data = but->active; + uiPopupBlockHandle *menu = data->menu; /* copy over return values from the closing menu */ if ((menu->menuretval & UI_RETURN_OK) || (menu->menuretval & UI_RETURN_UPDATE)) { @@ -9212,13 +9144,6 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, const int xy[2], const bool use_wiggle_room) { - float p1[2], p2[2], p3[2], p4[2]; - float oldp[2] = {menu->towards_xy[0], menu->towards_xy[1]}; - const float newp[2] = {xy[0], xy[1]}; - bool closer; - const float margin = MENU_TOWARDS_MARGIN; - rctf rect_px; - BLI_assert(block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)); /* annoying fix for [#36269], this is a bit odd but in fact works quite well @@ -9240,6 +9165,8 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, return false; } + float oldp[2] = {menu->towards_xy[0], menu->towards_xy[1]}; + const float newp[2] = {xy[0], xy[1]}; if (len_squared_v2v2(oldp, newp) < (4.0f * 4.0f)) { return menu->dotowards; } @@ -9247,19 +9174,15 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, /* verify that we are moving towards one of the edges of the * menu block, in other words, in the triangle formed by the * initial mouse location and two edge points. */ + rctf rect_px; ui_block_to_window_rctf(menu->region, block, &rect_px, &block->rect); - p1[0] = rect_px.xmin - margin; - p1[1] = rect_px.ymin - margin; - - p2[0] = rect_px.xmax + margin; - p2[1] = rect_px.ymin - margin; - - p3[0] = rect_px.xmax + margin; - p3[1] = rect_px.ymax + margin; + const float margin = MENU_TOWARDS_MARGIN; - p4[0] = rect_px.xmin - margin; - p4[1] = rect_px.ymax + margin; + const float p1[2] = {rect_px.xmin - margin, rect_px.ymin - margin}; + const float p2[2] = {rect_px.xmax + margin, rect_px.ymin - margin}; + const float p3[2] = {rect_px.xmax + margin, rect_px.ymax + margin}; + const float p4[2] = {rect_px.xmin - margin, rect_px.ymax + margin}; /* allow for some wiggle room, if the user moves a few pixels away, * don't immediately quit (only for top level menus) */ @@ -9272,8 +9195,9 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, add_v2_v2(oldp, delta); } - closer = (isect_point_tri_v2(newp, oldp, p1, p2) || isect_point_tri_v2(newp, oldp, p2, p3) || - isect_point_tri_v2(newp, oldp, p3, p4) || isect_point_tri_v2(newp, oldp, p4, p1)); + bool closer = (isect_point_tri_v2(newp, oldp, p1, p2) || + isect_point_tri_v2(newp, oldp, p2, p3) || + isect_point_tri_v2(newp, oldp, p3, p4) || isect_point_tri_v2(newp, oldp, p4, p1)); if (!closer) { menu->dotowards = false; @@ -9475,7 +9399,6 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock { ARegion *region = menu->region; uiBut *but = ui_region_find_active_but(region); - int retval; if (but) { /* Its possible there is an active menu item NOT under the mouse, @@ -9503,6 +9426,7 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock } } + int retval; if (but) { ScrArea *ctx_area = CTX_wm_area(C); ARegion *ctx_region = CTX_wm_region(C); @@ -9533,8 +9457,6 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2]) { float seg1[2]; - float seg2[2]; - float len; if (block->pie_data.flags & UI_PIE_INITIAL_DIRECTION) { copy_v2_v2(seg1, block->pie_data.pie_center_init); @@ -9543,9 +9465,10 @@ float ui_block_calc_pie_segment(uiBlock *block, const float event_xy[2]) copy_v2_v2(seg1, block->pie_data.pie_center_spawned); } + float seg2[2]; sub_v2_v2v2(seg2, event_xy, seg1); - len = normalize_v2_v2(block->pie_data.pie_dir, seg2); + const float len = normalize_v2_v2(block->pie_data.pie_dir, seg2); if (len < U.pie_menu_threshold * U.dpi_fac) { block->pie_data.flags |= UI_PIE_INVALID_DIR; @@ -9565,25 +9488,20 @@ static int ui_handle_menu_event(bContext *C, const bool is_parent_menu, const bool is_floating) { - ARegion *region; - uiBlock *block; uiBut *but; - int mx, my, retval; - bool inside; - bool inside_title; /* check for title dragging */ - - region = menu->region; - block = region->uiblocks.first; + ARegion *region = menu->region; + uiBlock *block = region->uiblocks.first; - retval = WM_UI_HANDLER_CONTINUE; + int retval = WM_UI_HANDLER_CONTINUE; - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(region, block, &mx, &my); /* check if mouse is inside block */ - inside = BLI_rctf_isect_pt(&block->rect, mx, my); - inside_title = inside && ((my + (UI_UNIT_Y * 1.5f)) > block->rect.ymax); + const bool inside = BLI_rctf_isect_pt(&block->rect, mx, my); + /* check for title dragging */ + const bool inside_title = inside && ((my + (UI_UNIT_Y * 1.5f)) > block->rect.ymax); /* if there's an active modal button, don't check events or outside, except for search menu */ but = ui_region_find_active_but(region); @@ -9762,7 +9680,7 @@ static int ui_handle_menu_event(bContext *C, if (val == KM_PRESS) { /* Determine scroll operation. */ uiMenuScrollType scrolltype; - bool ui_block_flipped = (block->flag & UI_BLOCK_IS_FLIP) != 0; + const bool ui_block_flipped = (block->flag & UI_BLOCK_IS_FLIP) != 0; if (ELEM(type, EVT_PAGEUPKEY, EVT_HOMEKEY)) { scrolltype = ui_block_flipped ? MENU_SCROLL_TOP : MENU_SCROLL_BOTTOM; @@ -10007,7 +9925,7 @@ static int ui_handle_menu_event(bContext *C, * Events handled above may have already set the return value, * don't overwrite them, see: T61015. */ - if ((inside == 0) && (menu->menuretval == 0)) { + if ((inside == false) && (menu->menuretval == 0)) { uiSafetyRct *saferct = block->saferct.first; if (ELEM(event->type, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) { @@ -10094,7 +10012,7 @@ static int ui_handle_menu_event(bContext *C, else { /* check mouse moving outside of the menu */ - if (inside == 0 && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) { + if (inside == false && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) { uiSafetyRct *saferct; ui_mouse_motion_towards_check(block, menu, &event->x, is_parent_inside == false); @@ -10173,21 +10091,15 @@ static int ui_handle_menu_return_submenu(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu) { - ARegion *region; - uiBut *but; - uiBlock *block; - uiHandleButtonData *data; - uiPopupBlockHandle *submenu; - - region = menu->region; - block = region->uiblocks.first; + ARegion *region = menu->region; + uiBlock *block = region->uiblocks.first; - but = ui_region_find_active_but(region); + uiBut *but = ui_region_find_active_but(region); BLI_assert(but); - data = but->active; - submenu = data->menu; + uiHandleButtonData *data = but->active; + uiPopupBlockHandle *submenu = data->menu; if (submenu->menuretval) { bool update; @@ -10234,7 +10146,7 @@ static int ui_but_pie_menu_apply(bContext *C, uiBut *but, bool force_close) { - int retval = WM_UI_HANDLER_BREAK; + const int retval = WM_UI_HANDLER_BREAK; if (but && ui_but_pie_menu_supported_apply(but)) { if (but->type == UI_BTYPE_MENU) { @@ -10282,13 +10194,11 @@ static uiBut *ui_block_pie_dir_activate(uiBlock *block, const wmEvent *event, Ra static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandle *menu) { - uiBut *active_but; - if (but == NULL) { return WM_UI_HANDLER_BREAK; } - active_but = ui_region_find_active_but(menu->region); + uiBut *active_but = ui_region_find_active_but(menu->region); if (active_but) { button_activate_exit(C, active_but, active_but->active, false, false); @@ -10300,13 +10210,6 @@ static int ui_but_pie_button_activate(bContext *C, uiBut *but, uiPopupBlockHandl static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle *menu) { - ARegion *region; - uiBlock *block; - float event_xy[2]; - double duration; - bool is_click_style; - float dist; - /* we block all events, this is modal interaction, * except for drop events which is described below */ int retval = WM_UI_HANDLER_BREAK; @@ -10317,10 +10220,10 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle retval = WM_UI_HANDLER_CONTINUE; } - region = menu->region; - block = region->uiblocks.first; + ARegion *region = menu->region; + uiBlock *block = region->uiblocks.first; - is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE); + const bool is_click_style = (block->pie_data.flags & UI_PIE_CLICK_STYLE); /* if there's an active modal button, don't check events or outside, except for search menu */ uiBut *but_active = ui_region_find_active_but(region); @@ -10331,15 +10234,14 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle menu->scrolltimer->duration = 0.0; } - duration = menu->scrolltimer->duration; + const double duration = menu->scrolltimer->duration; - event_xy[0] = event->x; - event_xy[1] = event->y; + float event_xy[2] = {event->x, event->y}; ui_window_to_block_fl(region, block, &event_xy[0], &event_xy[1]); /* Distance from initial point. */ - dist = ui_block_calc_pie_segment(block, event_xy); + const float dist = ui_block_calc_pie_segment(block, event_xy); if (but_active && button_modal_state(but_active->active->state)) { retval = ui_handle_menu_button(C, event, menu); @@ -10354,9 +10256,9 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle /* handle animation */ if (!(block->pie_data.flags & UI_PIE_ANIMATION_FINISHED)) { - double final_time = 0.01 * U.pie_animation_timeout; + const double final_time = 0.01 * U.pie_animation_timeout; float fac = duration / final_time; - float pie_radius = U.pie_menu_radius * UI_DPI_FAC; + const float pie_radius = U.pie_menu_radius * UI_DPI_FAC; if (fac > 1.0f) { fac = 1.0f; @@ -10446,7 +10348,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle switch (event->type) { case MOUSEMOVE: if (!is_click_style) { - float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init); + const float len_sq = len_squared_v2v2(event_xy, block->pie_data.pie_center_init); /* here we use the initial position explicitly */ if (len_sq > PIE_CLICK_THRESHOLD_SQ) { @@ -10569,16 +10471,13 @@ static int ui_handle_menus_recursive(bContext *C, const bool is_parent_menu, const bool is_floating) { - uiBut *but; - uiHandleButtonData *data; - uiPopupBlockHandle *submenu; int retval = WM_UI_HANDLER_CONTINUE; bool do_towards_reinit = false; /* check if we have a submenu, and handle events for it first */ - but = ui_region_find_active_but(menu->region); - data = (but) ? but->active : NULL; - submenu = (data) ? data->menu : NULL; + uiBut *but = ui_region_find_active_but(menu->region); + uiHandleButtonData *data = (but) ? but->active : NULL; + uiPopupBlockHandle *submenu = (data) ? data->menu : NULL; if (submenu) { uiBlock *block = menu->region->uiblocks.first; @@ -10586,14 +10485,13 @@ static int ui_handle_menus_recursive(bContext *C, bool inside = false; /* root pie menus accept the key that spawned * them as double click to improve responsiveness */ - bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) || event->type != block->pie_data.event); + const bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) || + event->type != block->pie_data.event); if (do_recursion) { if (is_parent_inside == false) { - int mx, my; - - mx = event->x; - my = event->y; + int mx = event->x; + int my = event->y; ui_window_to_block(menu->region, block, &mx, &my); inside = BLI_rctf_isect_pt(&block->rect, mx, my); } @@ -10613,7 +10511,7 @@ static int ui_handle_menus_recursive(bContext *C, (void)submenu; /* we may want to quit the submenu and handle the even in this menu, * if its important to use it, check 'data->menu' first */ - if (((retval == WM_UI_HANDLER_BREAK) && do_ret_out_parent) == 0) { + if (((retval == WM_UI_HANDLER_BREAK) && do_ret_out_parent) == false) { /* skip applying the event */ return retval; } @@ -10643,7 +10541,7 @@ static int ui_handle_menus_recursive(bContext *C, bool handled = false; if (listbox) { - int retval_test = ui_handle_list_event(C, event, menu->region, listbox); + const int retval_test = ui_handle_list_event(C, event, menu->region, listbox); if (retval_test != WM_UI_HANDLER_CONTINUE) { retval = retval_test; handled = true; @@ -10685,21 +10583,17 @@ void UI_popup_menu_retval_set(const uiBlock *block, const int retval, const bool static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(userdata)) { - ARegion *region; - uiBut *but, *listbox; - int retval; - /* here we handle buttons at the region level, non-modal */ - region = CTX_wm_region(C); - retval = WM_UI_HANDLER_CONTINUE; + ARegion *region = CTX_wm_region(C); + int retval = WM_UI_HANDLER_CONTINUE; if (region == NULL || BLI_listbase_is_empty(®ion->uiblocks)) { return retval; } /* either handle events for already activated button or try to activate */ - but = ui_region_find_active_but(region); - listbox = ui_list_find_mouse_over(region, event); + uiBut *but = ui_region_find_active_but(region); + uiBut *listbox = ui_list_find_mouse_over(region, event); retval = ui_handler_panel_region(C, event, region, listbox ? listbox : but); @@ -10736,17 +10630,14 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use static void ui_region_handler_remove(bContext *C, void *UNUSED(userdata)) { - bScreen *screen; - ARegion *region; - - region = CTX_wm_region(C); + ARegion *region = CTX_wm_region(C); if (region == NULL) { return; } UI_blocklist_free(C, ®ion->uiblocks); - screen = CTX_wm_screen(C); + bScreen *screen = CTX_wm_screen(C); if (screen == NULL) { return; } @@ -10765,18 +10656,16 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE { ARegion *menu_region = CTX_wm_menu(C); ARegion *region = menu_region ? menu_region : CTX_wm_region(C); - uiBut *but; int retval = WM_UI_HANDLER_CONTINUE; - but = ui_region_find_active_but(region); + uiBut *but = ui_region_find_active_but(region); if (but) { bScreen *screen = CTX_wm_screen(C); uiBut *but_other; - uiHandleButtonData *data; /* handle activated button events */ - data = but->active; + uiHandleButtonData *data = but->active; if ((data->state == BUTTON_STATE_MENU_OPEN) && /* Make sure this popup isn't dragging a button. @@ -10858,13 +10747,12 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) { uiPopupBlockHandle *menu = userdata; - struct ARegion *menu_region; /* we block all events, this is modal interaction, * except for drop events which is described below */ int retval = WM_UI_HANDLER_BREAK; bool reset_pie = false; - menu_region = CTX_wm_menu(C); + ARegion *menu_region = CTX_wm_menu(C); CTX_wm_menu_set(C, menu->region); if (event->type == EVT_DROP || event->val == KM_DBL_CLICK) { @@ -10886,7 +10774,7 @@ static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) if (menu->menuretval) { wmWindow *win = CTX_wm_window(C); /* copy values, we have to free first (closes region) */ - uiPopupBlockHandle temp = *menu; + const uiPopupBlockHandle temp = *menu; uiBlock *block = menu->region->uiblocks.first; /* set last pie event to allow chained pie spawning */ diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index edf66d82cbc..887f149ee12 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -264,9 +264,9 @@ static void viconutil_set_point(GLint pt[2], int x, int y) static void vicon_small_tri_right_draw(int x, int y, int w, int UNUSED(h), float alpha) { GLint pts[3][2]; - int cx = x + w / 2 - 4; - int cy = y + w / 2; - int d = w / 5, d2 = w / 7; + const int cx = x + w / 2 - 4; + const int cy = y + w / 2; + const int d = w / 5, d2 = w / 7; viconutil_set_point(pts[0], cx - d2, cy + d); viconutil_set_point(pts[1], cx - d2, cy - d); @@ -301,17 +301,17 @@ static void vicon_keytype_draw_wrapper( * while the draw_keyframe_shape() function needs the midpoint for * the keyframe */ - float xco = x + w / 2 + 0.5f; - float yco = y + h / 2 + 0.5f; + const float xco = x + w / 2 + 0.5f; + const float yco = y + h / 2 + 0.5f; GPUVertFormat *format = immVertexFormat(); - uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); uint color_id = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); uint outline_color_id = GPU_vertformat_attr_add( format, "outlineColor", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); - uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); + const uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); @@ -323,7 +323,7 @@ static void vicon_keytype_draw_wrapper( * - size: (default icon size == 16, default dopesheet icon size == 10) * - sel: true unless in handletype icons (so that "keyframe" state shows the iconic yellow icon) */ - bool sel = (handle_type == KEYFRAME_HANDLE_NONE); + const bool sel = (handle_type == KEYFRAME_HANDLE_NONE); draw_keyframe_shape(xco, yco, @@ -490,7 +490,7 @@ static void init_brush_icons(void) # define INIT_BRUSH_ICON(icon_id, name) \ { \ uchar *rect = (uchar *)datatoc_##name##_png; \ - int size = datatoc_##name##_png_size; \ + const int size = datatoc_##name##_png_size; \ DrawInfo *di; \ \ di = def_internal_icon(NULL, icon_id, 0, 0, w, ICON_TYPE_BUFFER, 0); \ @@ -732,7 +732,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, for (int y = 0; y < ICON_GRID_ROWS; y++) { for (int x = 0; x < ICON_GRID_COLS; x++) { - IconType icontype = icontypes[y * ICON_GRID_COLS + x]; + const IconType icontype = icontypes[y * ICON_GRID_COLS + x]; if (icontype.type != ICON_TYPE_MONO_TEXTURE) { continue; } @@ -743,7 +743,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, sy = sy / resolution_divider; /* blur the alpha channel and store it in blurred_alpha_buffer */ - int blur_size = 2 / resolution_divider; + const int blur_size = 2 / resolution_divider; for (int bx = 0; bx < icon_width; bx++) { const int asx = MAX2(bx - blur_size, 0); const int aex = MIN2(bx + blur_size + 1, icon_width); @@ -758,7 +758,7 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, for (int ax = asx; ax < aex; ax++) { for (int ay = asy; ay < aey; ay++) { const int offset_read = (sy + ay) * buf->x + (sx + ax); - uint color_read = buf->rect[offset_read]; + const uint color_read = buf->rect[offset_read]; const float alpha_read = ((color_read & 0xff000000) >> 24) / 255.0; alpha_accum += alpha_read; alpha_samples += 1; @@ -790,8 +790,8 @@ static ImBuf *create_mono_icon_with_border(ImBuf *buf, blend_color_interpolate_float(dest_rgba, orig_rgba, border_rgba, 1.0 - orig_rgba[3]); linearrgb_to_srgb_v4(dest_srgb, dest_rgba); - uint alpha_mask = ((uint)(dest_srgb[3] * 255)) << 24; - uint cpack = rgb_to_cpack(dest_srgb[0], dest_srgb[1], dest_srgb[2]) | alpha_mask; + const uint alpha_mask = ((uint)(dest_srgb[3] * 255)) << 24; + const uint cpack = rgb_to_cpack(dest_srgb[0], dest_srgb[1], dest_srgb[2]) | alpha_mask; result->rect[offset_write] = cpack; } } @@ -820,7 +820,7 @@ void UI_icons_reload_internal_textures(void) bTheme *btheme = UI_GetTheme(); ImBuf *b16buf = NULL, *b32buf = NULL, *b16buf_border = NULL, *b32buf_border = NULL; const float icon_border_intensity = btheme->tui.icon_border_intensity; - bool need_icons_with_border = icon_border_intensity > 0.0f; + const bool need_icons_with_border = icon_border_intensity > 0.0f; if (b16buf == NULL) { b16buf = IMB_ibImageFromMemory((const uchar *)datatoc_blender_icons16_png, @@ -936,7 +936,7 @@ static void init_internal_icons(void) for (y = 0; y < ICON_GRID_ROWS; y++) { /* Row W has monochrome icons. */ for (x = 0; x < ICON_GRID_COLS; x++) { - IconType icontype = icontypes[y * ICON_GRID_COLS + x]; + const IconType icontype = icontypes[y * ICON_GRID_COLS + x]; if (!ELEM(icontype.type, ICON_TYPE_COLOR_TEXTURE, ICON_TYPE_MONO_TEXTURE)) { continue; } @@ -1130,7 +1130,7 @@ void UI_icons_free_drawinfo(void *drawinfo) */ static DrawInfo *icon_create_drawinfo(Icon *icon) { - int icon_data_type = icon->obj_type; + const int icon_data_type = icon->obj_type; DrawInfo *di = NULL; di = MEM_callocN(sizeof(DrawInfo), "di_icon"); @@ -1246,7 +1246,7 @@ int UI_preview_render_size(enum eIconSizes size) */ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size) { - uint render_size = UI_preview_render_size(size); + const uint render_size = UI_preview_render_size(size); if (!prv_img) { if (G.debug & G_DEBUG) { @@ -1351,7 +1351,7 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi img->w = STUDIOLIGHT_ICON_SIZE; img->h = STUDIOLIGHT_ICON_SIZE; - size_t size = STUDIOLIGHT_ICON_SIZE * STUDIOLIGHT_ICON_SIZE * sizeof(uint); + const size_t size = STUDIOLIGHT_ICON_SIZE * STUDIOLIGHT_ICON_SIZE * sizeof(uint); img->rect = MEM_mallocN(size, __func__); memset(img->rect, 0, size); di->data.buffer.image = img; @@ -1479,7 +1479,7 @@ static void icon_draw_rect(float x, return; } /* modulate color */ - float col[4] = {alpha, alpha, alpha, alpha}; + const float col[4] = {alpha, alpha, alpha, alpha}; /* rect contains image in 'rendersize', we only scale if needed */ if (rw != w || rh != h) { @@ -1565,8 +1565,8 @@ static void icon_draw_cache_texture_flush_ex(GPUTexture *texture, GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_MULTI_RECT_COLOR); GPU_shader_bind(shader); - int img_binding = GPU_shader_get_texture_binding(shader, "image"); - int data_loc = GPU_shader_get_uniform(shader, "calls_data"); + const int img_binding = GPU_shader_get_texture_binding(shader, "image"); + const int data_loc = GPU_shader_get_uniform(shader, "calls_data"); GPU_texture_bind(texture, img_binding); GPU_sampler_icon_bind(img_binding); @@ -1703,10 +1703,10 @@ static void icon_draw_texture(float x, GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_IMAGE_RECT_COLOR); GPU_shader_bind(shader); - int img_binding = GPU_shader_get_texture_binding(shader, "image"); - int color_loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR); - int rect_tex_loc = GPU_shader_get_uniform(shader, "rect_icon"); - int rect_geom_loc = GPU_shader_get_uniform(shader, "rect_geom"); + const int img_binding = GPU_shader_get_texture_binding(shader, "image"); + const int color_loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_COLOR); + const int rect_tex_loc = GPU_shader_get_uniform(shader, "rect_icon"); + const int rect_geom_loc = GPU_shader_get_uniform(shader, "rect_geom"); if (rgb) { GPU_shader_uniform_vector(shader, color_loc, 4, 1, (float[4]){UNPACK3(rgb), alpha}); @@ -1838,7 +1838,7 @@ static void icon_draw_size(float x, } else if (di->type == ICON_TYPE_MONO_TEXTURE) { /* Monochrome icon that uses text or theme color. */ - bool with_border = mono_border && (btheme->tui.icon_border_intensity > 0.0f); + const bool with_border = mono_border && (btheme->tui.icon_border_intensity > 0.0f); float color[4]; if (mono_rgba) { rgba_uchar_to_float(color, (const uchar *)mono_rgba); @@ -2222,7 +2222,7 @@ int UI_rnaptr_icon_get(bContext *C, PointerRNA *ptr, int rnaicon, const bool big /* get icon from ID */ if (id) { - int icon = ui_id_icon_get(C, id, big); + const int icon = ui_id_icon_get(C, id, big); return icon ? icon : rnaicon; } @@ -2334,7 +2334,7 @@ void UI_icon_draw_ex(float x, const uchar mono_color[4], const bool mono_border) { - int draw_size = get_draw_size(ICON_SIZE_ICON); + const int draw_size = get_draw_size(ICON_SIZE_ICON); icon_draw_size(x, y, icon_id, diff --git a/source/blender/editors/interface/interface_icons_event.c b/source/blender/editors/interface/interface_icons_event.c index 4be2dbc0b4e..223fcbfd45b 100644 --- a/source/blender/editors/interface/interface_icons_event.c +++ b/source/blender/editors/interface/interface_icons_event.c @@ -85,8 +85,8 @@ static void icon_draw_rect_input_text(const rctf *rect, BLF_size(font_id, font_size * U.pixelsize, U.dpi); float width, height; BLF_width_and_height(font_id, str, BLF_DRAW_STR_DUMMY_MAX, &width, &height); - float x = rect->xmin + (((rect->xmax - rect->xmin) - width) / 2.0f); - float y = rect->ymin + (((rect->ymax - rect->ymin) - height) / 2.0f); + const float x = rect->xmin + (((rect->xmax - rect->xmin) - width) / 2.0f); + const float y = rect->ymin + (((rect->ymax - rect->ymin) - height) / 2.0f); BLF_position(font_id, x, y, 0.0f); BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX); BLF_batch_draw_flush(); @@ -98,8 +98,8 @@ static void icon_draw_rect_input_symbol(const rctf *rect, const float color[4], const int font_id = blf_mono_font; BLF_color4fv(font_id, color); BLF_size(font_id, 19 * U.pixelsize, U.dpi); - float x = rect->xmin + (2.0f * U.pixelsize); - float y = rect->ymin + (1.0f * U.pixelsize); + const float x = rect->xmin + (2.0f * U.pixelsize); + const float y = rect->ymin + (1.0f * U.pixelsize); BLF_position(font_id, x, y, 0.0f); BLF_draw(font_id, str, BLF_DRAW_STR_DUMMY_MAX); BLF_batch_draw_flush(); diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index eb1bd1ba42e..87be3745f87 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -794,8 +794,8 @@ extern int ui_handler_panel_region(struct bContext *C, const struct wmEvent *event, struct ARegion *region, const uiBut *active_but); -extern void ui_draw_aligned_panel(struct uiStyle *style, - uiBlock *block, +extern void ui_draw_aligned_panel(const struct uiStyle *style, + const uiBlock *block, const rcti *rect, const bool show_pin, const bool show_background); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index ad76466b67c..8184962a54b 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -225,7 +225,7 @@ typedef struct uiLayoutItemRoot { static const char *ui_item_name_add_colon(const char *name, char namestr[UI_MAX_NAME_STR]) { - int len = strlen(name); + const int len = strlen(name); if (len != 0 && len + 1 < UI_MAX_NAME_STR) { memcpy(namestr, name, len); @@ -251,7 +251,7 @@ static int ui_item_fit( return available - pos; } - float width = *extra_pixel + (item * available) / (float)all; + const float width = *extra_pixel + (item * available) / (float)all; *extra_pixel = width - (int)width; return (int)width; } @@ -262,7 +262,7 @@ static int ui_item_fit( return available - pos; } - float width = *extra_pixel + (item * available) / (float)all; + const float width = *extra_pixel + (item * available) / (float)all; *extra_pixel = width - (int)width; return (int)width; } @@ -457,8 +457,8 @@ static void ui_layer_but_cb(bContext *C, void *arg_but, void *arg_index) PointerRNA *ptr = &but->rnapoin; PropertyRNA *prop = but->rnaprop; int i, index = POINTER_AS_INT(arg_index); - int shift = win->eventstate->shift; - int len = RNA_property_array_length(ptr, prop); + const int shift = win->eventstate->shift; + const int len = RNA_property_array_length(ptr, prop); if (!shift) { RNA_property_boolean_set_index(ptr, prop, index, true); @@ -519,7 +519,7 @@ static void ui_item_array(uiLayout *layout, if (type == PROP_BOOLEAN && ELEM(subtype, PROP_LAYER, PROP_LAYER_MEMBER)) { /* special check for layer layout */ int butw, buth, unit; - int cols = (len >= 20) ? 2 : 1; + const int cols = (len >= 20) ? 2 : 1; const uint colbuts = len / (2 * cols); uint layer_used = 0; uint layer_active = 0; @@ -721,7 +721,7 @@ static void ui_item_enum_expand_handle(bContext *C, void *arg1, void *arg2) if (!win->eventstate->shift) { uiBut *but = (uiBut *)arg1; - int enum_value = POINTER_AS_INT(arg2); + const int enum_value = POINTER_AS_INT(arg2); int current_value = RNA_property_enum_get(&but->rnapoin, but->rnaprop); if (!(current_value & enum_value)) { @@ -814,7 +814,7 @@ static void ui_item_enum_expand_exec(uiLayout *layout, BLI_assert(RNA_property_type(prop) == PROP_ENUM); uiLayout *layout_radial = NULL; - bool radial = (layout->root->type == UI_LAYOUT_PIEMENU); + const bool radial = (layout->root->type == UI_LAYOUT_PIEMENU); if (radial) { RNA_property_enum_items_gettexted_all(block->evil_C, ptr, prop, &item_array, NULL, &free); } @@ -1179,7 +1179,7 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout, w = ui_text_icon_width(layout, name, icon, 0); - int prev_emboss = layout->emboss; + const int prev_emboss = layout->emboss; if (flag & UI_ITEM_R_NO_BG) { layout->emboss = UI_EMBOSS_NONE; } @@ -1223,7 +1223,7 @@ static uiBut *uiItemFullO_ptr_ex(uiLayout *layout, opptr->data = properties; } else { - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; opptr->data = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); } if (r_opptr) { @@ -2043,7 +2043,7 @@ void uiItemFullR(uiLayout *layout, if ((layout->root->type == UI_LAYOUT_MENU) || /* Use checkboxes only as a fallback in pie-menu's, when no icon is defined. */ ((layout->root->type == UI_LAYOUT_PIEMENU) && (icon == ICON_NONE))) { - int prop_flag = RNA_property_flag(prop); + const int prop_flag = RNA_property_flag(prop); if (type == PROP_BOOLEAN) { if ((is_array == false) || (index != RNA_NO_INDEX)) { if (prop_flag & PROP_ICONS_CONSECUTIVE) { @@ -2060,7 +2060,7 @@ void uiItemFullR(uiLayout *layout, } else if (type == PROP_ENUM) { if (index == RNA_ENUM_VALUE) { - int enum_value = RNA_property_enum_get(ptr, prop); + const int enum_value = RNA_property_enum_get(ptr, prop); if (prop_flag & PROP_ICONS_CONSECUTIVE) { icon = ICON_CHECKBOX_DEHLT; /* but->iconadd will set to correct icon */ } @@ -2099,7 +2099,7 @@ void uiItemFullR(uiLayout *layout, int w, h; ui_item_rna_size(layout, name, icon, ptr, prop, index, icon_only, compact, &w, &h); - int prev_emboss = layout->emboss; + const int prev_emboss = layout->emboss; if (no_bg) { layout->emboss = UI_EMBOSS_NONE; } @@ -3193,7 +3193,7 @@ uiLayout *uiItemL_respect_property_split(uiLayout *layout, const char *text, int { if (layout->item.flag & UI_ITEM_PROP_SEP) { uiBlock *block = uiLayoutGetBlock(layout); - uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout); + const uiPropertySplitWrapper split_wrapper = uiItemPropertySplitWrapperCreate(layout); /* Further items added to 'layout' will automatically be added to split_wrapper.property_row */ uiItemL_(split_wrapper.label_column, text, icon); @@ -3272,7 +3272,7 @@ void uiItemV(uiLayout *layout, const char *name, int icon, int argval) void uiItemS_ex(uiLayout *layout, float factor) { uiBlock *block = layout->root->block; - bool is_menu = ui_block_is_menu(block); + const bool is_menu = ui_block_is_menu(block); if (is_menu && !UI_block_can_add_separator(block)) { return; } @@ -3794,7 +3794,7 @@ static void ui_litem_layout_radial(uiLayout *litem) * also the old code at http://developer.blender.org/T5103 */ - int pie_radius = U.pie_menu_radius * UI_DPI_FAC; + const int pie_radius = U.pie_menu_radius * UI_DPI_FAC; x = litem->x; y = litem->y; diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index e47761236ca..82e6e01056c 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -201,7 +201,7 @@ static int copy_as_driver_button_exec(bContext *C, wmOperator *op) if (ptr.owner_id && ptr.data && prop) { ID *id; - int dim = RNA_property_array_dimension(&ptr, prop, NULL); + const int dim = RNA_property_array_dimension(&ptr, prop, NULL); char *path = RNA_path_from_real_ID_to_property_index(bmain, &ptr, prop, dim, index, &id); if (path) { @@ -378,7 +378,7 @@ static bool assign_default_button_poll(bContext *C) UI_context_active_but_prop_get(C, &ptr, &prop, &index); if (ptr.data && prop && RNA_property_editable(&ptr, prop)) { - PropertyType type = RNA_property_type(prop); + const PropertyType type = RNA_property_type(prop); return RNA_property_is_idprop(prop) && !RNA_property_array_check(prop) && ELEM(type, PROP_INT, PROP_FLOAT); @@ -1174,7 +1174,7 @@ bool ui_jump_to_target_button_poll(bContext *C) static int jump_to_target_button_exec(bContext *C, wmOperator *UNUSED(op)) { - bool success = jump_to_target_button(C, false); + const bool success = jump_to_target_button(C, false); return (success) ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } @@ -1474,16 +1474,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}; + const uiStringInfo but_label = {BUT_GET_LABEL, NULL}; + const uiStringInfo rna_label = {BUT_GET_RNA_LABEL, NULL}; + const uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; + const uiStringInfo but_tip = {BUT_GET_TIP, NULL}; + const uiStringInfo rna_tip = {BUT_GET_RNA_TIP, NULL}; + const uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; + const uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; + const uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; + const uiStringInfo rna_enum = {BUT_GET_RNAENUM_IDENTIFIER, NULL}; + const uiStringInfo rna_ctxt = {BUT_GET_RNA_LABEL_CONTEXT, NULL}; if (!BLI_is_dir(root)) { BKE_report(op->reports, diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index a70bcd208ab..ade77e96bf9 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -64,7 +64,9 @@ #include "interface_intern.h" -/*********************** defines and structs ************************/ +/* -------------------------------------------------------------------- */ +/** \name Defines & Structs + * \{ */ #define ANIMATION_TIME 0.30 #define ANIMATION_INTERVAL 0.02 @@ -76,15 +78,11 @@ #define PNL_NEW_ADDED 16 #define PNL_FIRST 32 -/* only show pin header button for pinned panels */ -#define USE_PIN_HIDDEN - /* the state of the mouse position relative to the panel */ typedef 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 */ - PANEL_MOUSE_INSIDE_SCALE, /* mouse is inside panel scale widget */ + 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 { @@ -121,6 +119,12 @@ static bool panel_type_context_poll(ARegion *region, const PanelType *panel_type, const char *context); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Local Functions + * \{ */ + static void panel_title_color_get(bool show_background, uchar color[4]) { if (show_background) { @@ -134,44 +138,9 @@ static void panel_title_color_get(bool show_background, uchar color[4]) } } -/*********************** space specific code ************************/ -/* temporary code to remove all sbuts stuff from panel code */ - -/* SpaceProperties.align */ -typedef enum eSpaceButtons_Align { - BUT_HORIZONTAL = 0, - BUT_VERTICAL = 1, - BUT_AUTO = 2, -} eSpaceButtons_Align; - -static int panel_aligned(const ScrArea *area, const ARegion *region) -{ - if (area->spacetype == SPACE_PROPERTIES && region->regiontype == RGN_TYPE_WINDOW) { - return BUT_VERTICAL; - } - if (area->spacetype == SPACE_USERPREF && region->regiontype == RGN_TYPE_WINDOW) { - return BUT_VERTICAL; - } - if (area->spacetype == SPACE_FILE && region->regiontype == RGN_TYPE_CHANNELS) { - return BUT_VERTICAL; - } - if (area->spacetype == SPACE_IMAGE && region->regiontype == RGN_TYPE_PREVIEW) { - return BUT_VERTICAL; - } - if (ELEM(region->regiontype, - RGN_TYPE_UI, - RGN_TYPE_TOOLS, - RGN_TYPE_TOOL_PROPS, - RGN_TYPE_HUD, - RGN_TYPE_NAV_BAR, - RGN_TYPE_EXECUTE)) { - return BUT_VERTICAL; - } - - return 0; -} - -static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, bool *no_animation) +static bool panel_active_animation_changed(ListBase *lb, + Panel **r_panel_animation, + bool *r_no_animation) { LISTBASE_FOREACH (Panel *, panel, lb) { /* Detect panel active flag changes. */ @@ -185,7 +154,7 @@ static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, b } if ((panel->runtime_flag & PNL_ACTIVE) && !(panel->flag & PNL_CLOSED)) { - if (panel_active_animation_changed(&panel->children, pa_animation, no_animation)) { + if (panel_active_animation_changed(&panel->children, r_panel_animation, r_no_animation)) { return true; } } @@ -194,15 +163,15 @@ static bool panel_active_animation_changed(ListBase *lb, Panel **pa_animation, b if (panel->activedata) { uiHandlePanelData *data = panel->activedata; if (data->state == PANEL_STATE_ANIMATION) { - *pa_animation = panel; + *r_panel_animation = panel; } else { /* Don't animate while handling other interaction. */ - *no_animation = true; + *r_no_animation = true; } } - if ((panel->runtime_flag & PNL_ANIM_ALIGN) && !(*pa_animation)) { - *pa_animation = panel; + if ((panel->runtime_flag & PNL_ANIM_ALIGN) && !(*r_panel_animation)) { + *r_panel_animation = panel; } } @@ -245,10 +214,13 @@ static bool panels_need_realign(ScrArea *area, ARegion *region, Panel **r_panel_ return false; } -/********* Functions for instanced panels. ***********/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Functions for Instanced Panels + * \{ */ -static Panel *UI_panel_add_instanced_ex(ScrArea *area, - ARegion *region, +static Panel *UI_panel_add_instanced_ex(ARegion *region, ListBase *panels, PanelType *panel_type, int list_index, @@ -265,7 +237,7 @@ static Panel *UI_panel_add_instanced_ex(ScrArea *area, * 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; - UI_panel_add_instanced_ex(area, region, &panel->children, child_type, list_index, custom_data); + UI_panel_add_instanced_ex(region, &panel->children, child_type, list_index, custom_data); } /* Make sure the panel is added to the end of the display-order as well. This is needed for @@ -288,14 +260,10 @@ static Panel *UI_panel_add_instanced_ex(ScrArea *area, /** * Called in situations where panels need to be added dynamically rather than having only one panel - * corresponding to each PanelType. + * corresponding to each #PanelType. */ -Panel *UI_panel_add_instanced(ScrArea *area, - ARegion *region, - ListBase *panels, - char *panel_idname, - int list_index, - PointerRNA *custom_data) +Panel *UI_panel_add_instanced( + ARegion *region, ListBase *panels, char *panel_idname, int list_index, PointerRNA *custom_data) { ARegionType *region_type = region->type; @@ -307,12 +275,12 @@ Panel *UI_panel_add_instanced(ScrArea *area, return NULL; } - return UI_panel_add_instanced_ex(area, region, panels, panel_type, list_index, custom_data); + return UI_panel_add_instanced_ex(region, panels, panel_type, list_index, custom_data); } /** - * Find a unique key to append to the idname for the lookup to the panel's #uiBlock. Needed for - * instanced panels, where there can be multiple with the same type and idname. + * Find a unique key to append to the #PanelTyype.idname for the lookup to the panel's #uiBlock. + * Needed for instanced panels, where there can be multiple with the same type and identifier. */ void UI_list_panel_unique_str(Panel *panel, char *r_name) { @@ -399,7 +367,7 @@ void UI_panels_free_instanced(const bContext *C, ARegion *region) * don't match in any way. * * \param data: The list of data to check against the instanced panels. - * \param panel_idname_func: Function to find the panel type idname for each item in the data list. + * \param panel_idname_func: Function to find the #PanelType.idname for each item in the data list. * For a readability and generality, this lookup happens separately for each type of panel list. */ bool UI_panel_list_matches_data(ARegion *region, @@ -529,14 +497,10 @@ static void reorder_instanced_panel_list(bContext *C, ARegion *region, Panel *dr */ static bool panel_set_expand_from_list_data_recursive(Panel *panel, short flag, short *flag_index) { - bool open = (flag & (1 << *flag_index)); - bool changed = (open == (bool)(panel->flag & PNL_CLOSEDY)); - if (open) { - panel->flag &= ~PNL_CLOSEDY; - } - else { - panel->flag |= PNL_CLOSEDY; - } + const bool open = (flag & (1 << *flag_index)); + bool changed = (open == (bool)(panel->flag & PNL_CLOSED)); + SET_FLAG_FROM_TEST(panel->flag, !open, PNL_CLOSED); + LISTBASE_FOREACH (Panel *, child, &panel->children) { *flag_index = *flag_index + 1; changed |= panel_set_expand_from_list_data_recursive(child, flag, flag_index); @@ -558,7 +522,7 @@ void UI_panel_set_expand_from_list_data(const bContext *C, Panel *panel) return; } - short expand_flag = panel->type->get_list_data_expand_flag(C, panel); + const short expand_flag = panel->type->get_list_data_expand_flag(C, panel); short flag_index = 0; /* Start panel animation if the open state was changed. */ @@ -572,13 +536,9 @@ void UI_panel_set_expand_from_list_data(const bContext *C, Panel *panel) */ static void get_panel_expand_flag(Panel *panel, short *flag, short *flag_index) { - bool open = !(panel->flag & PNL_CLOSEDY); - if (open) { - *flag |= (1 << *flag_index); - } - else { - *flag &= ~(1 << *flag_index); - } + const bool open = !(panel->flag & PNL_CLOSED); + SET_FLAG_FROM_TEST(*flag, open, (1 << *flag_index)); + LISTBASE_FOREACH (Panel *, child, &panel->children) { *flag_index = *flag_index + 1; get_panel_expand_flag(child, flag, flag_index); @@ -586,14 +546,14 @@ static void get_panel_expand_flag(Panel *panel, short *flag, short *flag_index) } /** - * Call the callback to store the panel and subpanel expansion settings in the list item that + * Call the callback to store the panel and sub-panel expansion settings in the list item that * corresponds to this panel. * * \note This needs to iterate through all of the regions panels because the panel with changed - * expansion could have been the subpanel of a instanced panel, meaning it might not know + * expansion could have been the sub-panel of a instanced panel, meaning it might not know * which list item it corresponds to. */ -static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region) +static void set_panels_list_data_expand_flag(const bContext *C, const ARegion *region) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { PanelType *panel_type = panel->type; @@ -613,7 +573,11 @@ static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region) } } -/****************************** panels ******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Panels + * \{ */ /** * Set flag state for a panel and its sub-panels. @@ -622,7 +586,7 @@ static void set_panels_list_data_expand_flag(const bContext *C, ARegion *region) */ static bool panel_set_flag_recursive(Panel *panel, int flag, bool value) { - short flag_original = panel->flag; + const short flag_original = panel->flag; SET_FLAG_FROM_TEST(panel->flag, value, flag); @@ -635,14 +599,10 @@ static bool panel_set_flag_recursive(Panel *panel, int flag, bool value) return changed; } -static void panels_collapse_all(const bContext *C, - ScrArea *area, - ARegion *region, - const Panel *from_panel) +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 int flag = ((panel_aligned(area, region) == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY); const PanelType *from_pt = from_panel->type; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { @@ -653,13 +613,11 @@ static void panels_collapse_all(const bContext *C, if (!pt->context[0] || !from_pt->context[0] || STREQ(pt->context, from_pt->context)) { if ((panel->flag & PNL_PIN) || !category || !pt->category[0] || STREQ(pt->category, category)) { - panel->flag &= ~PNL_CLOSED; - panel->flag |= flag; + panel->flag |= PNL_CLOSED; } } } } - set_panels_list_data_expand_flag(C, region); } static bool panel_type_context_poll(ARegion *region, @@ -692,19 +650,13 @@ Panel *UI_panel_find_by_type(ListBase *lb, PanelType *pt) /** * \note \a panel should be return value from #UI_panel_find_by_type and can be NULL. */ -Panel *UI_panel_begin(ScrArea *area, - ARegion *region, - ListBase *lb, - uiBlock *block, - PanelType *pt, - Panel *panel, - bool *r_open) +Panel *UI_panel_begin( + ARegion *region, ListBase *lb, uiBlock *block, PanelType *pt, Panel *panel, bool *r_open) { Panel *panel_last; const char *drawname = CTX_IFACE_(pt->translation_context, pt->label); const char *idname = pt->idname; const bool newpanel = (panel == NULL); - int align = panel_aligned(area, region); if (!newpanel) { panel->type = pt; @@ -716,12 +668,7 @@ Panel *UI_panel_begin(ScrArea *area, BLI_strncpy(panel->panelname, idname, sizeof(panel->panelname)); if (pt->flag & PNL_DEFAULT_CLOSED) { - if (align == BUT_VERTICAL) { - panel->flag |= PNL_CLOSEDY; - } - else { - panel->flag |= PNL_CLOSEDX; - } + panel->flag |= PNL_CLOSED; } panel->ofsx = 0; @@ -790,11 +737,10 @@ Panel *UI_panel_begin(ScrArea *area, return panel; } -static float panel_region_offset_x_get(const ARegion *region, int align) +static float panel_region_offset_x_get(const ARegion *region) { if (UI_panel_category_is_visible(region)) { - if (align == BUT_VERTICAL && - (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT)) { + if (RGN_ALIGN_ENUM_FROM_MASK(region->alignment) != RGN_ALIGN_RIGHT) { return UI_PANEL_CATEGORY_MARGIN_WIDTH; } } @@ -802,8 +748,7 @@ static float panel_region_offset_x_get(const ARegion *region, int align) return 0; } -void UI_panel_end( - const ScrArea *area, const ARegion *region, uiBlock *block, int width, int height, bool open) +void UI_panel_end(const ARegion *region, uiBlock *block, int width, int height, bool open) { Panel *panel = block->panel; @@ -826,8 +771,8 @@ void UI_panel_end( panel->sizey = height; } else { - int old_sizex = panel->sizex, old_sizey = panel->sizey; - int old_region_ofsx = panel->runtime.region_ofsx; + const int old_sizex = panel->sizex, old_sizey = panel->sizey; + const int old_region_ofsx = panel->runtime.region_ofsx; /* update width/height if non-zero */ if (width != 0) { @@ -843,8 +788,7 @@ void UI_panel_end( panel->ofsy += old_sizey - panel->sizey; } - int align = panel_aligned(area, region); - panel->runtime.region_ofsx = panel_region_offset_x_get(region, align); + panel->runtime.region_ofsx = panel_region_offset_x_get(region); if (old_region_ofsx != panel->runtime.region_ofsx) { panel->runtime_flag |= PNL_ANIM_ALIGN; } @@ -858,7 +802,7 @@ static void ui_offset_panel_block(uiBlock *block) /* compute bounds and offset */ ui_block_bounds_calc(block); - int ofsy = block->panel->sizey - style->panelspace; + const int ofsy = block->panel->sizey - style->panelspace; LISTBASE_FOREACH (uiBut *, but, &block->buttons) { but->rect.ymin += ofsy; @@ -870,14 +814,18 @@ static void ui_offset_panel_block(uiBlock *block) block->rect.xmin = block->rect.ymin = 0.0; } -/**************************** drawing *******************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Drawing + * \{ */ /* triangle 'icon' for panel header */ void UI_draw_icon_tri(float x, float y, char dir, const float color[4]) { - float f3 = 0.05 * U.widget_unit; - float f5 = 0.15 * U.widget_unit; - float f7 = 0.25 * U.widget_unit; + const float f3 = 0.05 * U.widget_unit; + const float f5 = 0.15 * U.widget_unit; + const float f7 = 0.25 * U.widget_unit; if (dir == 'h') { UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); @@ -906,57 +854,46 @@ void UI_panel_label_offset(uiBlock *block, int *r_x, int *r_y) } } -static void ui_draw_aligned_panel_header( - uiStyle *style, uiBlock *block, const rcti *rect, char dir, const bool show_background) +static void ui_draw_aligned_panel_header(const uiStyle *style, + const uiBlock *block, + const rcti *rect, + const bool show_background) { - Panel *panel = block->panel; - rcti hrect; - int pnl_icons; - const char *activename = panel->drawname; + const Panel *panel = block->panel; const bool is_subpanel = (panel->type && panel->type->parent); - uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle; - uchar col_title[4]; + const uiFontStyle *fontstyle = (is_subpanel) ? &style->widgetlabel : &style->paneltitle; /* + 0.001f to avoid flirting with float inaccuracy */ - pnl_icons = (panel->labelofs + (1.1f * PNL_ICON)) / block->aspect + 0.001f; + const int pnl_icons = (panel->labelofs + (1.1f * PNL_ICON)) / block->aspect + 0.001f; /* draw text label */ + uchar col_title[4]; panel_title_color_get(show_background, col_title); col_title[3] = 255; - hrect = *rect; - if (dir == 'h') { - hrect.xmin = rect->xmin + pnl_icons; - hrect.ymin -= 2.0f / block->aspect; - UI_fontstyle_draw(fontstyle, - &hrect, - activename, - col_title, - &(struct uiFontStyleDraw_Params){ - .align = UI_STYLE_TEXT_LEFT, - }); - } - else { - /* ignore 'pnl_icons', otherwise the text gets offset horizontally - * + 0.001f to avoid flirting with float inaccuracy - */ - hrect.xmin = rect->xmin + (PNL_ICON + 5) / block->aspect + 0.001f; - UI_fontstyle_draw_rotated(fontstyle, &hrect, activename, col_title); - } + rcti hrect = *rect; + hrect.xmin = rect->xmin + pnl_icons; + hrect.ymin -= 2.0f / block->aspect; + UI_fontstyle_draw(fontstyle, + &hrect, + panel->drawname, + col_title, + &(struct uiFontStyleDraw_Params){ + .align = UI_STYLE_TEXT_LEFT, + }); } -/* panel integrated in buttonswindow, tool/property lists etc */ -void ui_draw_aligned_panel(uiStyle *style, - uiBlock *block, +/** + * Panel integrated in buttons-window, tool/property lists etc + */ +void ui_draw_aligned_panel(const uiStyle *style, + const uiBlock *block, const rcti *rect, const bool show_pin, const bool show_background) { - Panel *panel = block->panel; - rctf itemrect; + const Panel *panel = block->panel; float color[4]; - const bool is_closed_x = (panel->flag & PNL_CLOSEDX) ? true : false; - const bool is_closed_y = (panel->flag & PNL_CLOSEDY) ? true : false; const bool is_subpanel = (panel->type && panel->type->parent); const bool show_drag = (!is_subpanel && /* FIXME(campbell): currently no background means floating panel which @@ -972,7 +909,8 @@ void ui_draw_aligned_panel(uiStyle *style, box_wcol = &btheme->tui.wcol_box; } - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); if (panel->type && (panel->type->flag & PNL_NO_HEADER)) { if (show_background) { @@ -984,7 +922,7 @@ void ui_draw_aligned_panel(uiStyle *style, return; } - /* Calculate header rect with + 0.001f to prevent flicker due to float inaccuracy */ + /* Calculate header rectangle with + 0.001f to prevent flicker due to float inaccuracy. */ rcti headrect = { rect->xmin, rect->xmax, rect->ymax, rect->ymax + floor(PNL_HEADER / block->aspect + 0.001f)}; @@ -993,12 +931,12 @@ void ui_draw_aligned_panel(uiStyle *style, /* Expand the top a tiny bit to give header buttons equal size above and below. */ rcti box_rect = {rect->xmin, rect->xmax, - (is_closed_x || is_closed_y) ? headrect.ymin : rect->ymin, + (panel->flag & PNL_CLOSED) ? headrect.ymin : rect->ymin, headrect.ymax + U.pixelsize}; ui_draw_box_opaque(&box_rect, UI_CNR_ALL); - /* Mimick the border between aligned box widgets for the bottom of the header. */ - if (!(is_closed_x || is_closed_y)) { + /* Mimic the border between aligned box widgets for the bottom of the header. */ + if (!(panel->flag & PNL_CLOSED)) { immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); GPU_blend(GPU_BLEND_ALPHA); @@ -1020,24 +958,23 @@ void ui_draw_aligned_panel(uiStyle *style, /* Draw the header backdrop. */ if (show_background && !is_subpanel && !draw_box_style) { - float minx = rect->xmin; - float maxx = is_closed_x ? (minx + PNL_HEADER / block->aspect) : rect->xmax; - float y = headrect.ymax; + const float minx = rect->xmin; + const float y = headrect.ymax; immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); GPU_blend(GPU_BLEND_ALPHA); /* draw with background color */ immUniformThemeColor(TH_PANEL_HEADER); - immRectf(pos, minx, headrect.ymin, maxx, y); + immRectf(pos, minx, headrect.ymin, rect->xmax, y); immBegin(GPU_PRIM_LINES, 4); immVertex2f(pos, minx, y); - immVertex2f(pos, maxx, y); + immVertex2f(pos, rect->xmax, y); immVertex2f(pos, minx, y); - immVertex2f(pos, maxx, y); + immVertex2f(pos, rect->xmax, y); immEnd(); @@ -1045,13 +982,8 @@ void ui_draw_aligned_panel(uiStyle *style, immUnbindProgram(); } -/* draw optional pin icon */ -#ifdef USE_PIN_HIDDEN - if (show_pin && (block->panel->flag & PNL_PIN)) -#else - if (show_pin) -#endif - { + /* draw optional pin icon */ + if (show_pin && (block->panel->flag & PNL_PIN)) { uchar col_title[4]; panel_title_color_get(show_background, col_title); @@ -1072,45 +1004,35 @@ void ui_draw_aligned_panel(uiStyle *style, if (is_subpanel) { titlerect.xmin += (0.7f * UI_UNIT_X) / block->aspect + 0.001f; } - if (is_closed_x == false) { - ui_draw_aligned_panel_header(style, block, &titlerect, 'h', show_background); + ui_draw_aligned_panel_header(style, block, &titlerect, show_background); - if (show_drag) { - /* itemrect smaller */ - const float scale = 0.7; - itemrect.xmax = headrect.xmax - (0.2f * UI_UNIT_X); - itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect); - itemrect.ymin = headrect.ymin; - itemrect.ymax = headrect.ymax; - BLI_rctf_scale(&itemrect, scale); + if (show_drag) { + /* Make `itemrect` smaller. */ + const float scale = 0.7; + rctf itemrect; + itemrect.xmax = headrect.xmax - (0.2f * UI_UNIT_X); + itemrect.xmin = itemrect.xmax - BLI_rcti_size_y(&headrect); + itemrect.ymin = headrect.ymin; + itemrect.ymax = headrect.ymax; + BLI_rctf_scale(&itemrect, scale); - GPU_matrix_push(); - GPU_matrix_translate_2f(itemrect.xmin, itemrect.ymin); + GPU_matrix_push(); + GPU_matrix_translate_2f(itemrect.xmin, itemrect.ymin); - const int col_tint = 84; - float col_high[4], col_dark[4]; - UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high); - UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark); + const int col_tint = 84; + float col_high[4], col_dark[4]; + UI_GetThemeColorShade4fv(TH_PANEL_HEADER, col_tint, col_high); + UI_GetThemeColorShade4fv(TH_PANEL_BACK, -col_tint, col_dark); - GPUBatch *batch = GPU_batch_preset_panel_drag_widget( - U.pixelsize, col_high, col_dark, BLI_rcti_size_y(&headrect) * scale); - GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR); - GPU_batch_draw(batch); - GPU_matrix_pop(); - } + GPUBatch *batch = GPU_batch_preset_panel_drag_widget( + U.pixelsize, col_high, col_dark, BLI_rcti_size_y(&headrect) * scale); + GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_FLAT_COLOR); + GPU_batch_draw(batch); + GPU_matrix_pop(); } /* Draw panel backdrop. */ - if (is_closed_y) { - /* skip */ - } - else if (is_closed_x) { - /* draw vertical title */ - ui_draw_aligned_panel_header(style, block, &headrect, 'v', show_background); - pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - } - /* an open panel */ - else { + if (!(panel->flag & PNL_CLOSED)) { /* in some occasions, draw a border */ if (panel->flag & PNL_SELECT && !is_subpanel) { float radius; @@ -1168,28 +1090,21 @@ void ui_draw_aligned_panel(uiStyle *style, immUnbindProgram(); } - uchar col_title[4]; - panel_title_color_get(show_background, col_title); - /* draw collapse icon */ - - /* itemrect smaller */ - itemrect.xmin = titlerect.xmin; - itemrect.xmax = itemrect.xmin + BLI_rcti_size_y(&titlerect); - itemrect.ymin = titlerect.ymin; - itemrect.ymax = titlerect.ymax; - - BLI_rctf_scale(&itemrect, 0.25f); - { + rctf itemrect = {.xmin = titlerect.xmin, + .xmax = itemrect.xmin + BLI_rcti_size_y(&titlerect), + .ymin = titlerect.ymin, + .ymax = titlerect.ymax}; + BLI_rctf_scale(&itemrect, 0.25f); + + uchar col_title[4]; + panel_title_color_get(show_background, col_title); float tria_color[4]; rgb_uchar_to_float(tria_color, col_title); tria_color[3] = 1.0f; - if (is_closed_y) { - ui_draw_anti_tria_rect(&itemrect, 'h', tria_color); - } - else if (is_closed_x) { + if (panel->flag & PNL_CLOSED) { ui_draw_anti_tria_rect(&itemrect, 'h', tria_color); } else { @@ -1198,17 +1113,434 @@ void ui_draw_aligned_panel(uiStyle *style, } } -/************************** panel alignment *************************/ +/** \} */ -static int get_panel_header(const Panel *panel) +/* -------------------------------------------------------------------- */ +/** \name Category Drawing (Tabs) + * \{ */ + +static void imm_buf_append( + float vbuf[][2], uchar cbuf[][3], float x, float y, const uchar col[3], int *index) { - if (panel->type && (panel->type->flag & PNL_NO_HEADER)) { - return 0; + ARRAY_SET_ITEMS(vbuf[*index], x, y); + ARRAY_SET_ITEMS(cbuf[*index], UNPACK3(col)); + (*index)++; +} + +/* based on UI_draw_roundbox, check on making a version which allows us to skip some sides */ +static void ui_panel_category_draw_tab(bool filled, + float minx, + float miny, + float maxx, + float maxy, + float rad, + const int roundboxtype, + const bool use_highlight, + const bool use_shadow, + const bool use_flip_x, + const uchar highlight_fade[3], + const uchar col[3]) +{ + float vec[4][2] = {{0.195, 0.02}, {0.55, 0.169}, {0.831, 0.45}, {0.98, 0.805}}; + + /* Multiply `vec` by radius. */ + for (int a = 0; a < 4; a++) { + mul_v2_fl(vec[a], rad); + } + + uint vert_len = 0; + if (use_highlight) { + vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 6 : 1; + vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 6 : 1; + } + if (use_highlight && !use_shadow) { + vert_len++; + } + else { + vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 6 : 1; + vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 6 : 1; + } + /* Maximum size. */ + float vbuf[24][2]; + uchar cbuf[24][3]; + int buf_index = 0; + + /* start with corner right-top */ + if (use_highlight) { + if (roundboxtype & UI_CNR_TOP_RIGHT) { + imm_buf_append(vbuf, cbuf, maxx, maxy - rad, col, &buf_index); + for (int a = 0; a < 4; a++) { + imm_buf_append(vbuf, cbuf, maxx - vec[a][1], maxy - rad + vec[a][0], col, &buf_index); + } + imm_buf_append(vbuf, cbuf, maxx - rad, maxy, col, &buf_index); + } + else { + imm_buf_append(vbuf, cbuf, maxx, maxy, col, &buf_index); + } + + /* corner left-top */ + if (roundboxtype & UI_CNR_TOP_LEFT) { + imm_buf_append(vbuf, cbuf, minx + rad, maxy, col, &buf_index); + for (int a = 0; a < 4; a++) { + imm_buf_append(vbuf, cbuf, minx + rad - vec[a][0], maxy - vec[a][1], col, &buf_index); + } + imm_buf_append(vbuf, cbuf, minx, maxy - rad, col, &buf_index); + } + else { + imm_buf_append(vbuf, cbuf, minx, maxy, col, &buf_index); + } + } + + if (use_highlight && !use_shadow) { + imm_buf_append( + vbuf, cbuf, minx, miny + rad, highlight_fade ? col : highlight_fade, &buf_index); + } + else { + /* corner left-bottom */ + if (roundboxtype & UI_CNR_BOTTOM_LEFT) { + imm_buf_append(vbuf, cbuf, minx, miny + rad, col, &buf_index); + for (int a = 0; a < 4; a++) { + imm_buf_append(vbuf, cbuf, minx + vec[a][1], miny + rad - vec[a][0], col, &buf_index); + } + imm_buf_append(vbuf, cbuf, minx + rad, miny, col, &buf_index); + } + else { + imm_buf_append(vbuf, cbuf, minx, miny, col, &buf_index); + } + + /* corner right-bottom */ + if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { + imm_buf_append(vbuf, cbuf, maxx - rad, miny, col, &buf_index); + for (int a = 0; a < 4; a++) { + imm_buf_append(vbuf, cbuf, maxx - rad + vec[a][0], miny + vec[a][1], col, &buf_index); + } + imm_buf_append(vbuf, cbuf, maxx, miny + rad, col, &buf_index); + } + else { + imm_buf_append(vbuf, cbuf, maxx, miny, col, &buf_index); + } + } + + if (use_flip_x) { + const float midx = (minx + maxx) / 2.0f; + for (int i = 0; i < buf_index; i++) { + vbuf[i][0] = midx - (vbuf[i][0] - midx); + } + } + + GPUVertFormat *format = immVertexFormat(); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add( + format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); + + immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); + immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_STRIP, vert_len); + for (int i = 0; i < buf_index; i++) { + immAttr3ubv(color, cbuf[i]); + immVertex2fv(pos, vbuf[i]); + } + immEnd(); + immUnbindProgram(); +} + +/** + * Draw vertical tabs on the left side of the region, + * one tab per category. + */ +void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) +{ + /* no tab outlines for */ + // #define USE_FLAT_INACTIVE + 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; + const int fontid = fstyle->uifont_id; + short fstyle_points = fstyle->points; + const float aspect = ((uiBlock *)region->uiblocks.first)->aspect; + const float zoom = 1.0f / aspect; + const int px = max_ii(1, round_fl_to_int(U.pixelsize)); + const int px_x_sign = is_left ? px : -px; + const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom); + const float dpi_fac = UI_DPI_FAC; + /* padding of tabs around text */ + const int tab_v_pad_text = round_fl_to_int((2 + ((px * 3) * dpi_fac)) * zoom); + /* padding between tabs */ + const int tab_v_pad = round_fl_to_int((4 + (2 * px * dpi_fac)) * zoom); + const float tab_curve_radius = ((px * 3) * dpi_fac) * zoom; + /* We flip the tab drawing, so always use these flags. */ + const int roundboxtype = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT; + bool is_alpha; + bool do_scaletabs = false; +#ifdef USE_FLAT_INACTIVE + bool is_active_prev = false; +#endif + float scaletabs = 1.0f; + /* same for all tabs */ + /* intentionally don't scale by 'px' */ + const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width); + const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3); + const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f; + + int y_ofs = tab_v_pad; + + /* Primary theme colors */ + uchar theme_col_back[4]; + uchar theme_col_text[3]; + uchar theme_col_text_hi[3]; + + /* Tab colors */ + uchar theme_col_tab_bg[4]; + uchar theme_col_tab_active[3]; + uchar theme_col_tab_inactive[3]; + + /* Secondary theme colors */ + uchar theme_col_tab_outline[3]; + uchar theme_col_tab_divider[3]; /* line that divides tabs from the main region */ + uchar theme_col_tab_highlight[3]; + uchar theme_col_tab_highlight_inactive[3]; + + UI_GetThemeColor4ubv(TH_BACK, theme_col_back); + UI_GetThemeColor3ubv(TH_TEXT, theme_col_text); + UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi); + + UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg); + UI_GetThemeColor3ubv(TH_TAB_ACTIVE, theme_col_tab_active); + UI_GetThemeColor3ubv(TH_TAB_INACTIVE, theme_col_tab_inactive); + UI_GetThemeColor3ubv(TH_TAB_OUTLINE, theme_col_tab_outline); + + interp_v3_v3v3_uchar(theme_col_tab_divider, theme_col_back, theme_col_tab_outline, 0.3f); + interp_v3_v3v3_uchar(theme_col_tab_highlight, theme_col_back, theme_col_text_hi, 0.2f); + interp_v3_v3v3_uchar( + theme_col_tab_highlight_inactive, theme_col_tab_inactive, theme_col_text_hi, 0.12f); + + is_alpha = (region->overlap && (theme_col_back[3] != 255)); + + if (fstyle->kerning == 1) { + BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + } + + BLF_enable(fontid, BLF_ROTATION); + BLF_rotation(fontid, M_PI_2); + // UI_fontstyle_set(&style->widget); + ui_fontscale(&fstyle_points, aspect / (U.pixelsize * 1.1f)); + BLF_size(fontid, fstyle_points, U.dpi); + + /* Check the region type supports categories to avoid an assert + * for showing 3D view panels in the properties space. */ + if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) { + BLI_assert(UI_panel_category_is_visible(region)); } - return PNL_HEADER; + /* Calculate tab rectangle and check if we need to scale down. */ + LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { + + rcti *rct = &pc_dyn->rect; + const char *category_id = pc_dyn->idname; + const char *category_id_draw = IFACE_(category_id); + const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); + + rct->xmin = rct_xmin; + rct->xmax = rct_xmax; + + rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2)); + rct->ymax = v2d->mask.ymax - (y_ofs); + + y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2); + } + + if (y_ofs > BLI_rcti_size_y(&v2d->mask)) { + scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs; + + LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { + rcti *rct = &pc_dyn->rect; + rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; + rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; + } + + do_scaletabs = true; + } + + /* begin drawing */ + GPU_line_smooth(true); + + uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + /* draw the background */ + if (is_alpha) { + GPU_blend(GPU_BLEND_ALPHA); + immUniformColor4ubv(theme_col_tab_bg); + } + else { + immUniformColor3ubv(theme_col_tab_bg); + } + + if (is_left) { + immRecti( + pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax); + } + else { + immRecti( + pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax); + } + + if (is_alpha) { + GPU_blend(GPU_BLEND_NONE); + } + + immUnbindProgram(); + + const int divider_xmin = is_left ? (v2d->mask.xmin + (category_tabs_width - px)) : + (v2d->mask.xmax - category_tabs_width) + px; + const int divider_xmax = is_left ? (v2d->mask.xmin + category_tabs_width) : + (v2d->mask.xmax - (category_tabs_width + px)) + px; + + LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { + const rcti *rct = &pc_dyn->rect; + const char *category_id = pc_dyn->idname; + const char *category_id_draw = IFACE_(category_id); + const int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2); + size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX; +#if 0 + int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); +#endif + + const bool is_active = STREQ(category_id, category_id_active); + + GPU_blend(GPU_BLEND_ALPHA); + +#ifdef USE_FLAT_INACTIVE + if (is_active) +#endif + { + const bool use_flip_x = !is_left; + ui_panel_category_draw_tab(true, + rct->xmin, + rct->ymin, + rct->xmax, + rct->ymax, + tab_curve_radius - px, + roundboxtype, + true, + true, + use_flip_x, + NULL, + is_active ? theme_col_tab_active : theme_col_tab_inactive); + + /* Tab outline */ + ui_panel_category_draw_tab(false, + rct->xmin - px_x_sign, + rct->ymin - px, + rct->xmax - px_x_sign, + rct->ymax + px, + tab_curve_radius, + roundboxtype, + true, + true, + use_flip_x, + NULL, + theme_col_tab_outline); + + /* Tab highlight (3d look) */ + ui_panel_category_draw_tab(false, + rct->xmin, + rct->ymin, + rct->xmax, + rct->ymax, + tab_curve_radius, + roundboxtype, + true, + false, + use_flip_x, + is_active ? theme_col_back : theme_col_tab_inactive, + is_active ? theme_col_tab_highlight : + theme_col_tab_highlight_inactive); + } + + /* Tab black-line. */ + if (!is_active) { + pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, divider_xmin, rct->ymin - tab_v_pad, divider_xmax, rct->ymax + tab_v_pad); + immUnbindProgram(); + } + + if (do_scaletabs) { + category_draw_len = BLF_width_to_strlen( + fontid, category_id_draw, category_draw_len, category_width, NULL); + } + + BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); + + /* Tab titles. */ + + /* Draw white shadow to give text more depth. */ + BLF_color3ubv(fontid, theme_col_text); + + /* Main tab title. */ + BLF_draw(fontid, category_id_draw, category_draw_len); + + GPU_blend(GPU_BLEND_NONE); + + /* Tab black-line remaining (last tab). */ + pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + if (pc_dyn->prev == NULL) { + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, divider_xmin, rct->ymax + px, divider_xmax, v2d->mask.ymax); + } + if (pc_dyn->next == NULL) { + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, divider_xmin, 0, divider_xmax, rct->ymin); + } + +#ifdef USE_FLAT_INACTIVE + /* Draw line between inactive tabs. */ + if (is_active == false && is_active_prev == false && pc_dyn->prev) { + immUniformColor3ubv(theme_col_tab_divider); + immRecti(pos, + v2d->mask.xmin + (category_tabs_width / 5), + rct->ymax + px, + (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5), + rct->ymax + (px * 3)); + } + + is_active_prev = is_active; +#endif + immUnbindProgram(); + + /* not essential, but allows events to be handled right up until the region edge [#38171] */ + if (is_left) { + pc_dyn->rect.xmin = v2d->mask.xmin; + } + else { + pc_dyn->rect.xmax = v2d->mask.xmax; + } + } + + GPU_line_smooth(false); + + BLF_disable(fontid, BLF_ROTATION); + + if (fstyle->kerning == 1) { + BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); + } + +#undef USE_FLAT_INACTIVE } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Panel Alignment + * \{ */ + static int get_panel_size_y(const Panel *panel) { if (panel->type && (panel->type->flag & PNL_NO_HEADER)) { @@ -1220,7 +1552,7 @@ static int get_panel_size_y(const Panel *panel) static int get_panel_real_size_y(const Panel *panel) { - int sizey = (panel->flag & PNL_CLOSED) ? 0 : panel->sizey; + const int sizey = (panel->flag & PNL_CLOSED) ? 0 : panel->sizey; if (panel->type && (panel->type->flag & PNL_NO_HEADER)) { return sizey; @@ -1234,24 +1566,18 @@ int UI_panel_size_y(const Panel *panel) return get_panel_real_size_y(panel); } -/* this function is needed because uiBlock and Panel itself don't - * change sizey or location when closed */ +/** + * This function is needed because #uiBlock and Panel itself don't + * change #Panel.sizey or location when closed. + */ static int get_panel_real_ofsy(Panel *panel) { - if (panel->flag & PNL_CLOSEDY) { + if (panel->flag & PNL_CLOSED) { return panel->ofsy + panel->sizey; } return panel->ofsy; } -static int get_panel_real_ofsx(Panel *panel) -{ - if (panel->flag & PNL_CLOSEDX) { - return panel->ofsx + get_panel_header(panel); - } - return panel->ofsx + panel->sizex; -} - bool UI_panel_is_dragging(const struct Panel *panel) { uiHandlePanelData *data = panel->activedata; @@ -1264,32 +1590,12 @@ bool UI_panel_is_dragging(const struct Panel *panel) /** * \note about sorting; - * the sortorder has a lower value for new panels being added. + * the #Panel.sortorder has a lower value for new panels being added. * however, that only works to insert a single panel, when more new panels get * added the coordinates of existing panels and the previously stored to-be-inserted * panels do not match for sorting */ -static int find_leftmost_panel(const void *a1, const void *a2) -{ - const PanelSort *ps1 = a1, *ps2 = a2; - - if (ps1->panel->ofsx > ps2->panel->ofsx) { - return 1; - } - if (ps1->panel->ofsx < ps2->panel->ofsx) { - return -1; - } - if (ps1->panel->sortorder > ps2->panel->sortorder) { - return 1; - } - if (ps1->panel->sortorder < ps2->panel->sortorder) { - return -1; - } - - return 0; -} - static int find_highest_panel(const void *a1, const void *a2) { const PanelSort *ps1 = a1, *ps2 = a2; @@ -1297,7 +1603,7 @@ static int find_highest_panel(const void *a1, const void *a2) /* stick uppermost header-less panels to the top of the region - * prevent them from being sorted (multiple header-less panels have to be sorted though) */ if (ps1->panel->type->flag & PNL_NO_HEADER && ps2->panel->type->flag & PNL_NO_HEADER) { - /* skip and check for ofs and sortorder below */ + /* Skip and check for `ofsy` and #Panel.sortorder below. */ } if (ps1->panel->type->flag & PNL_NO_HEADER) { return -1; @@ -1356,14 +1662,12 @@ static void align_sub_panels(Panel *panel) /* this doesn't draw */ /* returns 1 when it did something */ -static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, const bool drag) +static bool uiAlignPanelStep(ARegion *region, const float fac, const bool drag) { - PanelSort *ps, *panelsort, *psnext; - int a, tot = 0; - bool done; - int align = panel_aligned(area, region); + int i; /* count active, not tabbed panels */ + int tot = 0; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PNL_ACTIVE) { tot++; @@ -1374,22 +1678,10 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co return 0; } - /* extra; change close direction? */ - LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { - if (panel->runtime_flag & PNL_ACTIVE) { - if ((panel->flag & PNL_CLOSEDX) && (align == BUT_VERTICAL)) { - panel->flag ^= PNL_CLOSED; - } - else if ((panel->flag & PNL_CLOSEDY) && (align == BUT_HORIZONTAL)) { - panel->flag ^= PNL_CLOSED; - } - } - } - /* sort panels */ - panelsort = MEM_callocN(tot * sizeof(PanelSort), "panelsort"); + PanelSort *panelsort = MEM_callocN(tot * sizeof(PanelSort), "panelsort"); - ps = panelsort; + PanelSort *ps = panelsort; LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PNL_ACTIVE) { ps->panel = MEM_dupallocN(panel); @@ -1399,49 +1691,37 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co } if (drag) { - /* while we are dragging, we sort on location and update sortorder */ - if (align == BUT_VERTICAL) { - qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel); - } - else { - qsort(panelsort, tot, sizeof(PanelSort), find_leftmost_panel); - } + /* While we are dragging, we sort on location and update #Panel.sortorder. */ + qsort(panelsort, tot, sizeof(PanelSort), find_highest_panel); - for (ps = panelsort, a = 0; a < tot; a++, ps++) { - ps->orig->sortorder = a; + for (ps = panelsort, i = 0; i < tot; i++, ps++) { + ps->orig->sortorder = i; } } else { - /* otherwise use sortorder */ + /* Otherwise use #Panel.sortorder. */ qsort(panelsort, tot, sizeof(PanelSort), compare_panel); } - /* no smart other default start loc! this keeps switching f5/f6/etc compatible */ + /* No smart other default start location! This keeps switching f5/f6/etc compatible. */ ps = panelsort; - ps->panel->runtime.region_ofsx = panel_region_offset_x_get(region, align); + ps->panel->runtime.region_ofsx = panel_region_offset_x_get(region); ps->panel->ofsx = 0; ps->panel->ofsy = -get_panel_size_y(ps->panel); ps->panel->ofsx += ps->panel->runtime.region_ofsx; - for (a = 0; a < tot - 1; a++, ps++) { - psnext = ps + 1; + for (i = 0; i < tot - 1; i++, ps++) { + PanelSort *psnext = ps + 1; - if (align == BUT_VERTICAL) { - bool use_box = ps->panel->type && ps->panel->type->flag & PNL_DRAW_BOX; - bool use_box_next = psnext->panel->type && psnext->panel->type->flag & PNL_DRAW_BOX; - psnext->panel->ofsx = ps->panel->ofsx; - psnext->panel->ofsy = get_panel_real_ofsy(ps->panel) - get_panel_size_y(psnext->panel); + const bool use_box = ps->panel->type && ps->panel->type->flag & PNL_DRAW_BOX; + const bool use_box_next = psnext->panel->type && psnext->panel->type->flag & PNL_DRAW_BOX; + psnext->panel->ofsx = ps->panel->ofsx; + psnext->panel->ofsy = get_panel_real_ofsy(ps->panel) - get_panel_size_y(psnext->panel); - /* Extra margin for box style panels. */ - ps->panel->ofsx += (use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0.0f; - if (use_box || use_box_next) { - psnext->panel->ofsy -= UI_PANEL_BOX_STYLE_MARGIN; - } - } - else { - psnext->panel->ofsx = get_panel_real_ofsx(ps->panel); - psnext->panel->ofsy = ps->panel->ofsy + get_panel_size_y(ps->panel) - - get_panel_size_y(psnext->panel); + /* Extra margin for box style panels. */ + ps->panel->ofsx += (use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0.0f; + if (use_box || use_box_next) { + psnext->panel->ofsy -= UI_PANEL_BOX_STYLE_MARGIN; } } /* Extra margin for the last panel if it's a box-style panel. */ @@ -1450,16 +1730,16 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co } /* we interpolate */ - done = false; + bool changed = false; ps = panelsort; - for (a = 0; a < tot; a++, ps++) { + for (i = 0; i < tot; i++, ps++) { if ((ps->panel->flag & PNL_SELECT) == 0) { if ((ps->orig->ofsx != ps->panel->ofsx) || (ps->orig->ofsy != ps->panel->ofsy)) { ps->orig->ofsx = round_fl_to_int(fac * (float)ps->panel->ofsx + (1.0f - fac) * (float)ps->orig->ofsx); ps->orig->ofsy = round_fl_to_int(fac * (float)ps->panel->ofsy + (1.0f - fac) * (float)ps->orig->ofsy); - done = true; + changed = true; } } } @@ -1473,34 +1753,25 @@ static bool uiAlignPanelStep(ScrArea *area, ARegion *region, const float fac, co } } - /* free panelsort array */ - for (ps = panelsort, a = 0; a < tot; a++, ps++) { + /* Free `panelsort` array. */ + for (ps = panelsort, i = 0; i < tot; i++, ps++) { MEM_freeN(ps->panel); } MEM_freeN(panelsort); - return done; + return changed; } -static void ui_panels_size(ScrArea *area, ARegion *region, int *r_x, int *r_y) +static void ui_panels_size(ARegion *region, int *r_x, int *r_y) { - int align = panel_aligned(area, region); int sizex = 0; int sizey = 0; /* compute size taken up by panels, for setting in view2d */ LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->runtime_flag & PNL_ACTIVE) { - int pa_sizex, pa_sizey; - - if (align == BUT_VERTICAL) { - pa_sizex = panel->ofsx + panel->sizex; - pa_sizey = get_panel_real_ofsy(panel); - } - else { - pa_sizex = get_panel_real_ofsx(panel) + panel->sizex; - pa_sizey = panel->ofsy + get_panel_size_y(panel); - } + const int pa_sizex = panel->ofsx + panel->sizex; + const int pa_sizey = get_panel_real_ofsy(panel); sizex = max_ii(sizex, pa_sizex); sizey = min_ii(sizey, pa_sizey); @@ -1521,15 +1792,13 @@ static void ui_panels_size(ScrArea *area, ARegion *region, int *r_x, int *r_y) static void ui_do_animate(bContext *C, Panel *panel) { uiHandlePanelData *data = panel->activedata; - ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - float fac; - fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME; + float fac = (PIL_check_seconds_timer() - data->starttime) / ANIMATION_TIME; fac = min_ff(sqrtf(fac), 1.0f); /* for max 1 second, interpolate positions */ - if (uiAlignPanelStep(area, region, fac, false)) { + if (uiAlignPanelStep(region, fac, false)) { ED_region_tag_redraw(region); } else { @@ -1542,8 +1811,8 @@ static void ui_do_animate(bContext *C, Panel *panel) panel_activate_state(C, panel, PANEL_STATE_EXIT); if (is_drag_drop) { - /* Note: doing this in #panel_activate_state would require removing const for context in many - * other places. */ + /* Note: doing this in #panel_activate_state would require removing `const` for context in + * many other places. */ reorder_instanced_panel_list(C, region, panel); } return; @@ -1575,7 +1844,6 @@ void UI_panels_begin(const bContext *UNUSED(C), ARegion *region) void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) { ScrArea *area = CTX_wm_area(C); - Panel *panel, *panel_first; /* offset contents */ LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { @@ -1585,17 +1853,18 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) } /* re-align, possibly with animation */ + Panel *panel; if (panels_need_realign(area, region, &panel)) { if (panel) { panel_activate_state(C, panel, PANEL_STATE_ANIMATION); } else { - uiAlignPanelStep(area, region, 1.0, false); + uiAlignPanelStep(region, 1.0, false); } } /* tag first panel */ - panel_first = NULL; + Panel *panel_first = NULL; LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { if (block->active && block->panel) { if (!panel_first || block->panel->sortorder < panel_first->sortorder) { @@ -1609,15 +1878,11 @@ void UI_panels_end(const bContext *C, ARegion *region, int *r_x, int *r_y) } /* compute size taken up by panel */ - ui_panels_size(area, region, r_x, r_y); + ui_panels_size(region, r_x, r_y); } void UI_panels_draw(const bContext *C, ARegion *region) { - if (region->alignment != RGN_ALIGN_FLOAT) { - UI_ThemeClearColor(TH_BACK); - } - /* Draw panels, selected on top. Also in reverse order, because * UI blocks are added in reverse order and we need child panels * to draw on top. */ @@ -1638,7 +1903,7 @@ void UI_panels_scale(ARegion *region, float new_width) { LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { if (block->panel) { - float fac = new_width / (float)block->panel->sizex; + const float fac = new_width / (float)block->panel->sizex; block->panel->sizex = new_width; LISTBASE_FOREACH (uiBut *, but, &block->buttons) { @@ -1649,26 +1914,28 @@ void UI_panels_scale(ARegion *region, float new_width) } } -/************************ panel dragging ****************************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Panel Dragging + * \{ */ #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; - ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - short align = panel_aligned(area, region); /* Keep the drag position in the region with a small pad to keep the panel visible. */ - int x = clamp_i(event->x, region->winrct.xmin, region->winrct.xmax + DRAG_REGION_PAD); - int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD); + const int x = clamp_i(event->x, region->winrct.xmin, region->winrct.xmax + DRAG_REGION_PAD); + const int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD); float dx = (float)(x - data->startx); float dy = (float)(y - data->starty); /* Adjust for region zoom. */ - dx *= (float)BLI_rctf_size_x(®ion->v2d.cur) / (float)BLI_rcti_size_x(®ion->winrct); - dy *= (float)BLI_rctf_size_y(®ion->v2d.cur) / (float)BLI_rcti_size_y(®ion->winrct); + dx *= BLI_rctf_size_x(®ion->v2d.cur) / (float)BLI_rcti_size_x(®ion->winrct); + dy *= BLI_rctf_size_y(®ion->v2d.cur) / (float)BLI_rcti_size_y(®ion->winrct); if (data->state == PANEL_STATE_DRAG_SCALE) { panel->sizex = MAX2(data->startsizex + dx, UI_PANEL_MINX); @@ -1690,43 +1957,38 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel) panel->ofsx = data->startofsx + round_fl_to_int(dx); panel->ofsy = data->startofsy + round_fl_to_int(dy); - if (align) { - uiAlignPanelStep(area, region, 0.2f, true); - } + uiAlignPanelStep(region, 0.2f, true); } ED_region_tag_redraw(region); } #undef DRAG_REGION_PAD -/******************* region level panel interaction *****************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Region Level Panel Interaction + * \{ */ static uiPanelMouseState ui_panel_mouse_state_get(const uiBlock *block, const Panel *panel, const int mx, const int my) { - /* open panel */ - if (panel->flag & PNL_CLOSEDX) { - if ((block->rect.xmin <= mx) && (block->rect.xmin + PNL_HEADER >= mx)) { - return PANEL_MOUSE_INSIDE_HEADER; - } + if (!IN_RANGE((float)mx, block->rect.xmin, block->rect.xmax)) { + return PANEL_MOUSE_OUTSIDE; } - /* outside left/right side */ - else if ((block->rect.xmin > mx) || (block->rect.xmax < mx)) { - /* pass */ - } - else if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) { + + if (IN_RANGE((float)my, block->rect.ymax, block->rect.ymax + PNL_HEADER)) { return PANEL_MOUSE_INSIDE_HEADER; } - /* open panel */ - else if (!(panel->flag & PNL_CLOSEDY)) { - if ((block->rect.xmin <= mx) && (block->rect.xmax >= mx)) { - if ((block->rect.ymin <= my) && (block->rect.ymax + PNL_HEADER >= my)) { - return PANEL_MOUSE_INSIDE_CONTENT; - } + + if (!(panel->flag & PNL_CLOSED)) { + if (IN_RANGE((float)my, block->rect.ymin, block->rect.ymax + PNL_HEADER)) { + return PANEL_MOUSE_INSIDE_CONTENT; } } + return PANEL_MOUSE_OUTSIDE; } @@ -1741,55 +2003,38 @@ static void ui_panel_drag_collapse_handler_remove(bContext *UNUSED(C), void *use MEM_freeN(dragcol_data); } -static void ui_panel_drag_collapse(bContext *C, - uiPanelDragCollapseHandle *dragcol_data, +static void ui_panel_drag_collapse(const bContext *C, + const uiPanelDragCollapseHandle *dragcol_data, const int xy_dst[2]) { - ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); - Panel *panel; LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { float xy_a_block[2] = {UNPACK2(dragcol_data->xy_init)}; float xy_b_block[2] = {UNPACK2(xy_dst)}; - rctf rect = block->rect; - int oldflag; - const bool is_horizontal = (panel_aligned(area, region) == BUT_HORIZONTAL); + Panel *panel = block->panel; - if ((panel = block->panel) == 0 || (panel->type && (panel->type->flag & PNL_NO_HEADER))) { + if (panel == NULL || (panel->type && (panel->type->flag & PNL_NO_HEADER))) { continue; } - oldflag = panel->flag; + const int oldflag = panel->flag; - /* lock one axis */ - if (is_horizontal) { - xy_b_block[1] = dragcol_data->xy_init[1]; - } - else { - xy_b_block[0] = dragcol_data->xy_init[0]; - } + /* lock axis */ + xy_b_block[0] = dragcol_data->xy_init[0]; /* use cursor coords in block space */ ui_window_to_block_fl(region, block, &xy_a_block[0], &xy_a_block[1]); ui_window_to_block_fl(region, block, &xy_b_block[0], &xy_b_block[1]); - /* set up rect to match header size */ + /* Set up `rect` to match header size. */ + rctf rect = block->rect; rect.ymin = rect.ymax; rect.ymax = rect.ymin + PNL_HEADER; - if (panel->flag & PNL_CLOSEDX) { - rect.xmax = rect.xmin + PNL_HEADER; - } /* touch all panels between last mouse coord and the current one */ if (BLI_rctf_isect_segment(&rect, xy_a_block, xy_b_block)) { - /* force panel to close */ - if (dragcol_data->was_first_open == true) { - panel->flag |= (is_horizontal ? PNL_CLOSEDX : PNL_CLOSEDY); - } - /* force panel to open */ - else { - panel->flag &= ~PNL_CLOSED; - } + /* Force panel to open or close. */ + SET_FLAG_FROM_TEST(panel->flag, dragcol_data->was_first_open, PNL_CLOSED); /* if panel->flag has changed this means a panel was opened/closed here */ if (panel->flag != oldflag) { @@ -1853,145 +2098,112 @@ static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was 0); } -/* this function is supposed to call general window drawing too */ -/* also it supposes a block has panel, and isn't a menu */ -static void ui_handle_panel_header( - const bContext *C, uiBlock *block, int mx, int my, int event, short ctrl, short shift) +/** + * Supposing the block has a panel and isn't a menu, handle opening, closing, pinning, etc. + * Code currently assumes layout style for location of widgets + * + * \param mx The mouse x coordinate, in panel space. + */ +static void ui_handle_panel_header(const bContext *C, + uiBlock *block, + const int mx, + short int event_type, + const short ctrl, + const short shift) { - ScrArea *area = CTX_wm_area(C); + Panel *panel = block->panel; ARegion *region = CTX_wm_region(C); -#ifdef USE_PIN_HIDDEN - const bool show_pin = UI_panel_category_is_visible(region) && - (block->panel->type->parent == NULL) && (block->panel->flag & PNL_PIN); -#else - const bool show_pin = UI_panel_category_is_visible(region) && - (block->panel->type->parent == NULL); -#endif - const bool is_subpanel = (block->panel->type && block->panel->type->parent); - const bool show_drag = !is_subpanel; - int align = panel_aligned(area, region), button = 0; + BLI_assert(panel->type != NULL); + BLI_assert(!(panel->type->flag & PNL_NO_HEADER)); - rctf rect_drag, rect_pin; - float rect_leftmost; + const bool is_subpanel = (panel->type->parent != NULL); + const bool use_pin = UI_panel_category_is_visible(region) && !is_subpanel; + const bool show_pin = use_pin && (panel->flag & PNL_PIN); + const bool show_drag = !is_subpanel; - /* drag and pin rect's */ - rect_drag = block->rect; - rect_drag.xmin = block->rect.xmax - (PNL_ICON * 1.5f); - rect_pin = rect_drag; - if (show_pin) { - BLI_rctf_translate(&rect_pin, -PNL_ICON, 0.0f); + /* Handle panel pinning. */ + if (use_pin && ELEM(event_type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE) && shift) { + panel->flag ^= PNL_PIN; + ED_region_tag_redraw(region); + return; } - rect_leftmost = rect_pin.xmin; - - /* mouse coordinates in panel space! */ - /* XXX weak code, currently it assumes layout style for location of widgets */ - - /* check open/collapsed button */ - if (event == EVT_RETKEY) { - button = 1; - } - else if (event == EVT_AKEY) { - button = 1; - } - else if (ELEM(event, 0, EVT_RETKEY, LEFTMOUSE) && shift) { - if (block->panel->type->parent == NULL) { - block->panel->flag ^= PNL_PIN; - button = 2; - } + float expansion_area_xmax = block->rect.xmax; + if (show_drag) { + expansion_area_xmax -= (PNL_ICON * 1.5f); } - else if (block->panel->flag & PNL_CLOSEDX) { - if (my >= block->rect.ymax) { - button = 1; - } - } - else if (mx < rect_leftmost) { - button = 1; + if (show_pin) { + expansion_area_xmax -= PNL_ICON; } - if (button) { - if (button == 2) { /* close */ - ED_region_tag_redraw(region); - } - else { - /* Collapse and expand panels. */ - - if (ctrl) { - /* For parent panels, collapse all other panels or toggle children. */ - if (block->panel->type != NULL && block->panel->type->parent == NULL) { - if (block->panel->flag & PNL_CLOSED || BLI_listbase_is_empty(&block->panel->children)) { - panels_collapse_all(C, area, region, block->panel); + /* Collapse and expand panels. */ + if (ELEM(event_type, EVT_RETKEY, EVT_PADENTER, EVT_AKEY) || mx < expansion_area_xmax) { + if (ctrl && !is_subpanel) { + /* For parent panels, collapse all other panels or toggle children. */ + if (panel->flag & PNL_CLOSED || BLI_listbase_is_empty(&panel->children)) { + panels_collapse_all(region, panel); - /* Reset the view - we don't want to display a view without content. */ - UI_view2d_offset(®ion->v2d, 0.0f, 1.0f); - } - else { - const int closed_flag = (align == BUT_HORIZONTAL) ? PNL_CLOSEDX : PNL_CLOSEDY; - /* If a panel has sub-panels and it's open, toggle the expansion - * of the sub-panels (based on the expansion of the first subpanel). */ - Panel *first_child = block->panel->children.first; - BLI_assert(first_child != NULL); - panel_set_flag_recursive( - block->panel, closed_flag, (first_child->flag & PNL_CLOSED) == 0); - block->panel->flag |= closed_flag; - } - } + /* Reset the view - we don't want to display a view without content. */ + UI_view2d_offset(®ion->v2d, 0.0f, 1.0f); } - - if (block->panel->flag & PNL_CLOSED) { - block->panel->flag &= ~PNL_CLOSED; - /* snap back up so full panel aligns with screen edge */ - if (block->panel->snap & PNL_SNAP_BOTTOM) { - block->panel->ofsy = 0; - } - - if (event == LEFTMOUSE) { - ui_panel_drag_collapse_handler_add(C, false); - } + 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_set_flag_recursive(panel, PNL_CLOSED, !(first_child->flag & PNL_CLOSED)); + panel->flag |= PNL_CLOSED; } - else if (align == BUT_HORIZONTAL) { - block->panel->flag |= PNL_CLOSEDX; + } - if (event == LEFTMOUSE) { - ui_panel_drag_collapse_handler_add(C, true); - } + if (panel->flag & PNL_CLOSED) { + panel->flag &= ~PNL_CLOSED; + /* Snap back up so full panel aligns with screen edge. */ + if (panel->snap & PNL_SNAP_BOTTOM) { + panel->ofsy = 0; } - else { - /* snap down to bottom screen edge */ - block->panel->flag |= PNL_CLOSEDY; - if (block->panel->snap & PNL_SNAP_BOTTOM) { - block->panel->ofsy = -block->panel->sizey; - } - if (event == LEFTMOUSE) { - ui_panel_drag_collapse_handler_add(C, true); - } + if (event_type == LEFTMOUSE) { + ui_panel_drag_collapse_handler_add(C, false); } - - set_panels_list_data_expand_flag(C, region); - } - - if (align) { - panel_activate_state(C, block->panel, PANEL_STATE_ANIMATION); } else { - /* FIXME: this doesn't update the panel drawing, assert to avoid debugging why this is. - * We could fix this in the future if it's ever needed. */ - BLI_assert(0); - ED_region_tag_redraw(region); + /* Snap down to bottom screen edge. */ + panel->flag |= PNL_CLOSED; + if (panel->snap & PNL_SNAP_BOTTOM) { + panel->ofsy = -panel->sizey; + } + + if (event_type == LEFTMOUSE) { + ui_panel_drag_collapse_handler_add(C, true); + } } + + set_panels_list_data_expand_flag(C, region); + panel_activate_state(C, panel, PANEL_STATE_ANIMATION); + return; } - else if (show_drag && BLI_rctf_isect_x(&rect_drag, mx)) { - /* XXX, for now don't allow dragging in floating windows yet. */ - if (region->alignment == RGN_ALIGN_FLOAT) { + + /* Handle panel dragging. For now don't allow dragging in floating regions. */ + if (show_drag && !(region->alignment == RGN_ALIGN_FLOAT)) { + const float drag_area_xmin = block->rect.xmax - (PNL_ICON * 1.5f); + const float drag_area_xmax = block->rect.xmax; + if (IN_RANGE(mx, drag_area_xmin, drag_area_xmax)) { + panel_activate_state(C, panel, PANEL_STATE_DRAG); return; } - panel_activate_state(C, block->panel, PANEL_STATE_DRAG); } - else if (show_pin && BLI_rctf_isect_x(&rect_pin, mx)) { - block->panel->flag ^= PNL_PIN; - ED_region_tag_redraw(region); + + /* Handle panel unpinning. */ + if (show_pin) { + const float pin_area_xmin = expansion_area_xmax; + const float pin_area_xmax = pin_area_xmin + PNL_ICON; + if (IN_RANGE(mx, pin_area_xmin, pin_area_xmax)) { + panel->flag ^= PNL_PIN; + ED_region_tag_redraw(region); + return; + } } } @@ -2027,16 +2239,16 @@ static void ui_panel_category_active_set(ARegion *region, const char *idname, bo } if (fallback) { - /* For fallbacks, add at the end so explicitly chosen categories have priority. */ + /* For fall-backs, add at the end so explicitly chosen categories have priority. */ BLI_addtail(lb, pc_act); } else { BLI_addhead(lb, pc_act); } - /* validate all active panels, we could do this on load, + /* Validate all active panels, we could do this on load, * they are harmless - but we should remove somewhere. - * (addons could define own and gather cruft over time) */ + * (add-ons could define own and gather cruft over time). */ { PanelCategoryStack *pc_act_next; /* intentionally skip first */ @@ -2114,421 +2326,6 @@ void UI_panel_category_clear_all(ARegion *region) BLI_freelistN(®ion->panels_category); } -static void imm_buf_append( - float vbuf[][2], uchar cbuf[][3], float x, float y, const uchar col[3], int *index) -{ - ARRAY_SET_ITEMS(vbuf[*index], x, y); - ARRAY_SET_ITEMS(cbuf[*index], UNPACK3(col)); - (*index)++; -} - -/* based on UI_draw_roundbox, check on making a version which allows us to skip some sides */ -static void ui_panel_category_draw_tab(bool filled, - float minx, - float miny, - float maxx, - float maxy, - float rad, - const int roundboxtype, - const bool use_highlight, - const bool use_shadow, - const bool use_flip_x, - const uchar highlight_fade[3], - const uchar col[3]) -{ - float vec[4][2] = {{0.195, 0.02}, {0.55, 0.169}, {0.831, 0.45}, {0.98, 0.805}}; - int a; - - /* mult */ - for (a = 0; a < 4; a++) { - mul_v2_fl(vec[a], rad); - } - - uint vert_len = 0; - if (use_highlight) { - vert_len += (roundboxtype & UI_CNR_TOP_RIGHT) ? 6 : 1; - vert_len += (roundboxtype & UI_CNR_TOP_LEFT) ? 6 : 1; - } - if (use_highlight && !use_shadow) { - vert_len++; - } - else { - vert_len += (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 6 : 1; - vert_len += (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 6 : 1; - } - /* Maximum size. */ - float vbuf[24][2]; - uchar cbuf[24][3]; - int buf_index = 0; - - /* start with corner right-top */ - if (use_highlight) { - if (roundboxtype & UI_CNR_TOP_RIGHT) { - imm_buf_append(vbuf, cbuf, maxx, maxy - rad, col, &buf_index); - for (a = 0; a < 4; a++) { - imm_buf_append(vbuf, cbuf, maxx - vec[a][1], maxy - rad + vec[a][0], col, &buf_index); - } - imm_buf_append(vbuf, cbuf, maxx - rad, maxy, col, &buf_index); - } - else { - imm_buf_append(vbuf, cbuf, maxx, maxy, col, &buf_index); - } - - /* corner left-top */ - if (roundboxtype & UI_CNR_TOP_LEFT) { - imm_buf_append(vbuf, cbuf, minx + rad, maxy, col, &buf_index); - for (a = 0; a < 4; a++) { - imm_buf_append(vbuf, cbuf, minx + rad - vec[a][0], maxy - vec[a][1], col, &buf_index); - } - imm_buf_append(vbuf, cbuf, minx, maxy - rad, col, &buf_index); - } - else { - imm_buf_append(vbuf, cbuf, minx, maxy, col, &buf_index); - } - } - - if (use_highlight && !use_shadow) { - imm_buf_append( - vbuf, cbuf, minx, miny + rad, highlight_fade ? col : highlight_fade, &buf_index); - } - else { - /* corner left-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_LEFT) { - imm_buf_append(vbuf, cbuf, minx, miny + rad, col, &buf_index); - for (a = 0; a < 4; a++) { - imm_buf_append(vbuf, cbuf, minx + vec[a][1], miny + rad - vec[a][0], col, &buf_index); - } - imm_buf_append(vbuf, cbuf, minx + rad, miny, col, &buf_index); - } - else { - imm_buf_append(vbuf, cbuf, minx, miny, col, &buf_index); - } - - /* corner right-bottom */ - if (roundboxtype & UI_CNR_BOTTOM_RIGHT) { - imm_buf_append(vbuf, cbuf, maxx - rad, miny, col, &buf_index); - for (a = 0; a < 4; a++) { - imm_buf_append(vbuf, cbuf, maxx - rad + vec[a][0], miny + vec[a][1], col, &buf_index); - } - imm_buf_append(vbuf, cbuf, maxx, miny + rad, col, &buf_index); - } - else { - imm_buf_append(vbuf, cbuf, maxx, miny, col, &buf_index); - } - } - - if (use_flip_x) { - float midx = (minx + maxx) / 2.0f; - for (int i = 0; i < buf_index; i++) { - vbuf[i][0] = midx - (vbuf[i][0] - midx); - } - } - - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add( - format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); - - immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); - immBegin(filled ? GPU_PRIM_TRI_FAN : GPU_PRIM_LINE_STRIP, vert_len); - for (int i = 0; i < buf_index; i++) { - immAttr3ubv(color, cbuf[i]); - immVertex2fv(pos, vbuf[i]); - } - immEnd(); - immUnbindProgram(); -} - -/** - * Draw vertical tabs on the left side of the region, - * one tab per category. - */ -void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) -{ - /* no tab outlines for */ - // #define USE_FLAT_INACTIVE - 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; - const int fontid = fstyle->uifont_id; - short fstyle_points = fstyle->points; - const float aspect = ((uiBlock *)region->uiblocks.first)->aspect; - const float zoom = 1.0f / aspect; - const int px = max_ii(1, round_fl_to_int(U.pixelsize)); - const int px_x_sign = is_left ? px : -px; - const int category_tabs_width = round_fl_to_int(UI_PANEL_CATEGORY_MARGIN_WIDTH * zoom); - const float dpi_fac = UI_DPI_FAC; - /* padding of tabs around text */ - const int tab_v_pad_text = round_fl_to_int((2 + ((px * 3) * dpi_fac)) * zoom); - /* padding between tabs */ - const int tab_v_pad = round_fl_to_int((4 + (2 * px * dpi_fac)) * zoom); - const float tab_curve_radius = ((px * 3) * dpi_fac) * zoom; - /* We flip the tab drawing, so always use these flags. */ - const int roundboxtype = UI_CNR_TOP_LEFT | UI_CNR_BOTTOM_LEFT; - bool is_alpha; - bool do_scaletabs = false; -#ifdef USE_FLAT_INACTIVE - bool is_active_prev = false; -#endif - float scaletabs = 1.0f; - /* same for all tabs */ - /* intentionally dont scale by 'px' */ - const int rct_xmin = is_left ? v2d->mask.xmin + 3 : (v2d->mask.xmax - category_tabs_width); - const int rct_xmax = is_left ? v2d->mask.xmin + category_tabs_width : (v2d->mask.xmax - 3); - const int text_v_ofs = (rct_xmax - rct_xmin) * 0.3f; - - int y_ofs = tab_v_pad; - - /* Primary theme colors */ - uchar theme_col_back[4]; - uchar theme_col_text[3]; - uchar theme_col_text_hi[3]; - - /* Tab colors */ - uchar theme_col_tab_bg[4]; - uchar theme_col_tab_active[3]; - uchar theme_col_tab_inactive[3]; - - /* Secondary theme colors */ - uchar theme_col_tab_outline[3]; - uchar theme_col_tab_divider[3]; /* line that divides tabs from the main region */ - uchar theme_col_tab_highlight[3]; - uchar theme_col_tab_highlight_inactive[3]; - - UI_GetThemeColor4ubv(TH_BACK, theme_col_back); - UI_GetThemeColor3ubv(TH_TEXT, theme_col_text); - UI_GetThemeColor3ubv(TH_TEXT_HI, theme_col_text_hi); - - UI_GetThemeColor4ubv(TH_TAB_BACK, theme_col_tab_bg); - UI_GetThemeColor3ubv(TH_TAB_ACTIVE, theme_col_tab_active); - UI_GetThemeColor3ubv(TH_TAB_INACTIVE, theme_col_tab_inactive); - UI_GetThemeColor3ubv(TH_TAB_OUTLINE, theme_col_tab_outline); - - interp_v3_v3v3_uchar(theme_col_tab_divider, theme_col_back, theme_col_tab_outline, 0.3f); - interp_v3_v3v3_uchar(theme_col_tab_highlight, theme_col_back, theme_col_text_hi, 0.2f); - interp_v3_v3v3_uchar( - theme_col_tab_highlight_inactive, theme_col_tab_inactive, theme_col_text_hi, 0.12f); - - is_alpha = (region->overlap && (theme_col_back[3] != 255)); - - if (fstyle->kerning == 1) { - BLF_enable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - - BLF_enable(fontid, BLF_ROTATION); - BLF_rotation(fontid, M_PI_2); - // UI_fontstyle_set(&style->widget); - ui_fontscale(&fstyle_points, aspect / (U.pixelsize * 1.1f)); - BLF_size(fontid, fstyle_points, U.dpi); - - /* Check the region type supports categories to avoid an assert - * for showing 3D view panels in the properties space. */ - if ((1 << region->regiontype) & RGN_TYPE_HAS_CATEGORY_MASK) { - BLI_assert(UI_panel_category_is_visible(region)); - } - - /* calculate tab rect's and check if we need to scale down */ - LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { - - rcti *rct = &pc_dyn->rect; - const char *category_id = pc_dyn->idname; - const char *category_id_draw = IFACE_(category_id); - const int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); - - rct->xmin = rct_xmin; - rct->xmax = rct_xmax; - - rct->ymin = v2d->mask.ymax - (y_ofs + category_width + (tab_v_pad_text * 2)); - rct->ymax = v2d->mask.ymax - (y_ofs); - - y_ofs += category_width + tab_v_pad + (tab_v_pad_text * 2); - } - - if (y_ofs > BLI_rcti_size_y(&v2d->mask)) { - scaletabs = (float)BLI_rcti_size_y(&v2d->mask) / (float)y_ofs; - - LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { - rcti *rct = &pc_dyn->rect; - rct->ymin = ((rct->ymin - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; - rct->ymax = ((rct->ymax - v2d->mask.ymax) * scaletabs) + v2d->mask.ymax; - } - - do_scaletabs = true; - } - - /* begin drawing */ - GPU_line_smooth(true); - - uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - /* draw the background */ - if (is_alpha) { - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ubv(theme_col_tab_bg); - } - else { - immUniformColor3ubv(theme_col_tab_bg); - } - - if (is_left) { - immRecti( - pos, v2d->mask.xmin, v2d->mask.ymin, v2d->mask.xmin + category_tabs_width, v2d->mask.ymax); - } - else { - immRecti( - pos, v2d->mask.xmax - category_tabs_width, v2d->mask.ymin, v2d->mask.xmax, v2d->mask.ymax); - } - - if (is_alpha) { - GPU_blend(GPU_BLEND_NONE); - } - - immUnbindProgram(); - - const int divider_xmin = is_left ? (v2d->mask.xmin + (category_tabs_width - px)) : - (v2d->mask.xmax - category_tabs_width) + px; - const int divider_xmax = is_left ? (v2d->mask.xmin + category_tabs_width) : - (v2d->mask.xmax - (category_tabs_width + px)) + px; - - LISTBASE_FOREACH (PanelCategoryDyn *, pc_dyn, ®ion->panels_category) { - const rcti *rct = &pc_dyn->rect; - const char *category_id = pc_dyn->idname; - const char *category_id_draw = IFACE_(category_id); - int category_width = BLI_rcti_size_y(rct) - (tab_v_pad_text * 2); - size_t category_draw_len = BLF_DRAW_STR_DUMMY_MAX; - // int category_width = BLF_width(fontid, category_id_draw, BLF_DRAW_STR_DUMMY_MAX); - - const bool is_active = STREQ(category_id, category_id_active); - - GPU_blend(GPU_BLEND_ALPHA); - -#ifdef USE_FLAT_INACTIVE - if (is_active) -#endif - { - const bool use_flip_x = !is_left; - ui_panel_category_draw_tab(true, - rct->xmin, - rct->ymin, - rct->xmax, - rct->ymax, - tab_curve_radius - px, - roundboxtype, - true, - true, - use_flip_x, - NULL, - is_active ? theme_col_tab_active : theme_col_tab_inactive); - - /* tab outline */ - ui_panel_category_draw_tab(false, - rct->xmin - px_x_sign, - rct->ymin - px, - rct->xmax - px_x_sign, - rct->ymax + px, - tab_curve_radius, - roundboxtype, - true, - true, - use_flip_x, - NULL, - theme_col_tab_outline); - - /* tab highlight (3d look) */ - ui_panel_category_draw_tab(false, - rct->xmin, - rct->ymin, - rct->xmax, - rct->ymax, - tab_curve_radius, - roundboxtype, - true, - false, - use_flip_x, - is_active ? theme_col_back : theme_col_tab_inactive, - is_active ? theme_col_tab_highlight : - theme_col_tab_highlight_inactive); - } - - /* tab blackline */ - if (!is_active) { - pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor3ubv(theme_col_tab_divider); - immRecti(pos, divider_xmin, rct->ymin - tab_v_pad, divider_xmax, rct->ymax + tab_v_pad); - immUnbindProgram(); - } - - if (do_scaletabs) { - category_draw_len = BLF_width_to_strlen( - fontid, category_id_draw, category_draw_len, category_width, NULL); - } - - BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); - - /* tab titles */ - - /* draw white shadow to give text more depth */ - BLF_color3ubv(fontid, theme_col_text); - - /* main tab title */ - BLF_draw(fontid, category_id_draw, category_draw_len); - - GPU_blend(GPU_BLEND_NONE); - - /* tab blackline remaining (last tab) */ - pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - if (pc_dyn->prev == NULL) { - immUniformColor3ubv(theme_col_tab_divider); - immRecti(pos, divider_xmin, rct->ymax + px, divider_xmax, v2d->mask.ymax); - } - if (pc_dyn->next == NULL) { - immUniformColor3ubv(theme_col_tab_divider); - immRecti(pos, divider_xmin, 0, divider_xmax, rct->ymin); - } - -#ifdef USE_FLAT_INACTIVE - /* draw line between inactive tabs */ - if (is_active == false && is_active_prev == false && pc_dyn->prev) { - immUniformColor3ubv(theme_col_tab_divider); - immRecti(pos, - v2d->mask.xmin + (category_tabs_width / 5), - rct->ymax + px, - (v2d->mask.xmin + category_tabs_width) - (category_tabs_width / 5), - rct->ymax + (px * 3)); - } - - is_active_prev = is_active; -#endif - immUnbindProgram(); - - /* not essential, but allows events to be handled right up until the region edge [#38171] */ - if (is_left) { - pc_dyn->rect.xmin = v2d->mask.xmin; - } - else { - pc_dyn->rect.xmax = v2d->mask.xmax; - } - } - - GPU_line_smooth(false); - - BLF_disable(fontid, BLF_ROTATION); - - if (fstyle->kerning == 1) { - BLF_disable(fstyle->uifont_id, BLF_KERNING_DEFAULT); - } - -#undef USE_FLAT_INACTIVE -} - static int ui_handle_panel_category_cycling(const wmEvent *event, ARegion *region, const uiBut *active_but) @@ -2581,132 +2378,108 @@ static int ui_handle_panel_category_cycling(const wmEvent *event, return WM_UI_HANDLER_CONTINUE; } -/* XXX should become modal keymap */ -/* AKey is opening/closing panels, independent of button state now */ - +/** + * Handle region panel events like opening and closing panels, changing categories, etc. + * + * \note Could become a modal key-map. + */ int ui_handler_panel_region(bContext *C, const wmEvent *event, ARegion *region, const uiBut *active_but) { - Panel *panel; - int retval, mx, my; - bool has_category_tabs = UI_panel_category_is_visible(region); + /* Mouse-move events are handled by separate handlers for dragging and drag collapsing. */ + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { + return WM_UI_HANDLER_CONTINUE; + } - retval = WM_UI_HANDLER_CONTINUE; + /* We only use KM_PRESS events in this function, so it's simpler to return early. */ + if (event->val != KM_PRESS) { + return WM_UI_HANDLER_CONTINUE; + } - /* Scrollbars can overlap panels now, they have handling priority. */ + /* Scroll-bars can overlap panels now, they have handling priority. */ if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->x, event->y)) { - return retval; + return WM_UI_HANDLER_CONTINUE; } - /* handle category tabs */ - if (has_category_tabs) { - if (event->val == KM_PRESS) { - if (event->type == LEFTMOUSE) { - PanelCategoryDyn *pc_dyn = UI_panel_category_find_mouse_over(region, event); - if (pc_dyn) { - UI_panel_category_active_set(region, pc_dyn->idname); - ED_region_tag_redraw(region); + int retval = WM_UI_HANDLER_CONTINUE; - /* reset scroll to the top [#38348] */ - UI_view2d_offset(®ion->v2d, -1.0f, 1.0f); + /* Handle category tabs. */ + if (UI_panel_category_is_visible(region)) { + if (event->type == LEFTMOUSE) { + PanelCategoryDyn *pc_dyn = UI_panel_category_find_mouse_over(region, event); + if (pc_dyn) { + UI_panel_category_active_set(region, pc_dyn->idname); + ED_region_tag_redraw(region); - retval = WM_UI_HANDLER_BREAK; - } - } - else if ((event->type == EVT_TABKEY && event->ctrl) || - ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) { - /* cycle tabs */ - retval = ui_handle_panel_category_cycling(event, region, active_but); + /* Reset scroll to the top (T38348). */ + UI_view2d_offset(®ion->v2d, -1.0f, 1.0f); + + retval = WM_UI_HANDLER_BREAK; } } + else if ((event->type == EVT_TABKEY && event->ctrl) || + ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE)) { + /* Cycle tabs. */ + retval = ui_handle_panel_category_cycling(event, region, active_but); + } } if (retval == WM_UI_HANDLER_BREAK) { return retval; } - LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - uiPanelMouseState mouse_state; - - mx = event->x; - my = event->y; - ui_window_to_block(region, block, &mx, &my); - - /* checks for mouse position inside */ - panel = block->panel; + const bool region_has_active_button = (ui_region_find_active_but(region) != NULL); - if (!panel) { + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + Panel *panel = block->panel; + if (panel == NULL || panel->type == NULL) { continue; } - /* XXX - accessed freed panels when scripts reload, need to fix. */ - if (panel->type && panel->type->flag & PNL_NO_HEADER) { + /* We can't expand or collapse panels without headers, they would disappear. */ + if (panel->type->flag & PNL_NO_HEADER) { continue; } - mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); - - /* XXX hardcoded key warning */ - if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER) && - event->val == KM_PRESS) { - if (event->type == EVT_AKEY && - ((event->ctrl + event->oskey + event->shift + event->alt) == 0)) { + int mx = event->x; + int my = event->y; + ui_window_to_block(region, block, &mx, &my); - if (panel->flag & PNL_CLOSEDY) { - if ((block->rect.ymax <= my) && (block->rect.ymax + PNL_HEADER >= my)) { - ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift); - } - } - else { - ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift); - } + const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); + /* The panel collapse / expand key "A" is special as it takes priority over + * active button handling. */ + if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { + if (event->type == EVT_AKEY && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { retval = WM_UI_HANDLER_BREAK; - continue; + ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift); + break; } } - /* on active button, do not handle panels */ - if (ui_region_find_active_but(region) != NULL) { + /* Don't do any other panel handling with an active button. */ + if (region_has_active_button) { continue; } - if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { - - if (event->val == KM_PRESS) { + /* All mouse clicks inside panels should return in break, but continue handling + * in case there is a sub-panel header at the mouse location. */ + if (event->type == LEFTMOUSE && + ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { + retval = WM_UI_HANDLER_BREAK; + } - /* open close on header */ - if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER)) { - if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { - ui_handle_panel_header(C, block, mx, my, EVT_RETKEY, event->ctrl, event->shift); - retval = WM_UI_HANDLER_BREAK; - break; - } - } - else if (event->type == LEFTMOUSE) { - /* all inside clicks should return in break - overlapping/float panels */ - retval = WM_UI_HANDLER_BREAK; - - if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { - ui_handle_panel_header(C, block, mx, my, event->type, event->ctrl, event->shift); - retval = WM_UI_HANDLER_BREAK; - break; - } - if ((mouse_state == PANEL_MOUSE_INSIDE_SCALE) && !(panel->flag & PNL_CLOSED)) { - panel_activate_state(C, panel, PANEL_STATE_DRAG_SCALE); - retval = WM_UI_HANDLER_BREAK; - break; - } - } - else if (event->type == RIGHTMOUSE) { - if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { - ui_popup_context_menu_for_panel(C, region, block->panel); - retval = WM_UI_HANDLER_BREAK; - break; - } - } + if (mouse_state == PANEL_MOUSE_INSIDE_HEADER) { + if (ELEM(event->type, EVT_RETKEY, EVT_PADENTER, LEFTMOUSE)) { + retval = WM_UI_HANDLER_BREAK; + ui_handle_panel_header(C, block, mx, event->type, event->ctrl, event->shift); + } + else if (event->type == RIGHTMOUSE) { + retval = WM_UI_HANDLER_BREAK; + ui_popup_context_menu_for_panel(C, region, block->panel); } + break; } } @@ -2748,7 +2521,7 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm int mx = event->x; int my = event->y; ui_window_to_block(region, block, &mx, &my); - int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); + const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { break; } @@ -2763,7 +2536,11 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm return customdata; } -/**************** window level modal panel interaction **************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Window Level Modal Panel Interaction + * \{ */ /* note, this is modal handler and should not swallow events for animation */ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) @@ -2773,16 +2550,7 @@ static int ui_handler_panel(bContext *C, const wmEvent *event, void *userdata) /* verify if we can stop */ if (event->type == LEFTMOUSE && event->val == KM_RELEASE) { - ScrArea *area = CTX_wm_area(C); - ARegion *region = CTX_wm_region(C); - int align = panel_aligned(area, region); - - if (align) { - panel_activate_state(C, panel, PANEL_STATE_ANIMATION); - } - else { - panel_activate_state(C, panel, PANEL_STATE_EXIT); - } + panel_activate_state(C, panel, PANEL_STATE_ANIMATION); } else if (event->type == MOUSEMOVE) { if (data->state == PANEL_STATE_DRAG) { @@ -2823,7 +2591,7 @@ static void panel_activate_state(const bContext *C, Panel *panel, uiHandlePanelS return; } - bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG); + const bool was_drag_drop = (data && data->state == PANEL_STATE_DRAG); /* Set selection state for the panel and its sub-panels, which need to know they are selected * too so they can be drawn above their parent when it's dragged. */ @@ -2897,3 +2665,5 @@ PanelType *UI_paneltype_find(int space_id, int region_id, const char *idname) } return NULL; } + +/** \} */ diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 8648a54f7d5..9dcfe8c872b 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -256,7 +256,7 @@ bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEv rect.xmax = rect.xmin + (BLI_rcti_size_y(&rect)); } else { - int delta = BLI_rcti_size_x(&rect) - BLI_rcti_size_y(&rect); + const int delta = BLI_rcti_size_x(&rect) - BLI_rcti_size_y(&rect); rect.xmin += delta / 2; rect.xmax -= delta / 2; } @@ -315,7 +315,7 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) } /* Currently no need to expose this at the moment. */ - bool labeledit = true; + const bool labeledit = true; rctf rect_px_fl; BLI_rctf_rcti_copy(&rect_px_fl, rect_px); uiBut *butover = NULL; diff --git a/source/blender/editors/interface/interface_region_color_picker.c b/source/blender/editors/interface/interface_region_color_picker.c index 8bc0f18886b..de80d6270bf 100644 --- a/source/blender/editors/interface/interface_region_color_picker.c +++ b/source/blender/editors/interface/interface_region_color_picker.c @@ -351,7 +351,7 @@ static void ui_colorpicker_hide_reveal(uiBlock *block, enum ePickerType colormod static void ui_colorpicker_create_mode_cb(bContext *UNUSED(C), void *bt1, void *UNUSED(arg)) { uiBut *bt = bt1; - short colormode = ui_but_value_get(bt); + const short colormode = ui_but_value_get(bt); ui_colorpicker_hide_reveal(bt->block, colormode); } diff --git a/source/blender/editors/interface/interface_region_hud.c b/source/blender/editors/interface/interface_region_hud.c index 1773a7b3057..cecfe6941fc 100644 --- a/source/blender/editors/interface/interface_region_hud.c +++ b/source/blender/editors/interface/interface_region_hud.c @@ -178,7 +178,7 @@ static void hud_region_layout(const bContext *C, ARegion *region) } ScrArea *area = CTX_wm_area(C); - int size_y = region->sizey; + const int size_y = region->sizey; ED_region_panels_layout(C, region); @@ -201,7 +201,7 @@ static void hud_region_layout(const bContext *C, ARegion *region) region->winrct.xmax = (region->winrct.xmin + region->winx) - 1; region->winrct.ymax = (region->winrct.ymin + region->winy) - 1; - UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_PANELS_UI, region->winx, region->winy); + UI_view2d_region_reinit(v2d, V2D_COMMONVIEW_LIST, region->winx, region->winy); /* Weak, but needed to avoid glitches, especially with hi-dpi * (where resizing the view glitches often). @@ -217,8 +217,7 @@ static void hud_region_draw(const bContext *C, ARegion *region) { UI_view2d_view_ortho(®ion->v2d); wmOrtho2_region_pixelspace(region); - GPU_clear_color(0, 0, 0, 0.0f); - GPU_clear(GPU_COLOR_BIT); + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); if ((region->flag & RGN_FLAG_HIDDEN) == 0) { ui_draw_menu_back(NULL, @@ -317,7 +316,7 @@ void ED_area_type_hud_ensure(bContext *C, ScrArea *area) } bool init = false; - bool was_hidden = region == NULL || region->visible == false; + const bool was_hidden = region == NULL || region->visible == false; ARegion *region_op = CTX_wm_region(C); BLI_assert((region_op == NULL) || (region_op->regiontype != RGN_TYPE_HUD)); if (!last_redo_poll(C, region_op ? region_op->regiontype : -1)) { diff --git a/source/blender/editors/interface/interface_region_menu_pie.c b/source/blender/editors/interface/interface_region_menu_pie.c index 1371c7524ae..631f395390f 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.c +++ b/source/blender/editors/interface/interface_region_menu_pie.c @@ -387,7 +387,7 @@ void ui_pie_menu_level_create(uiBlock *block, { const int totitem_parent = PIE_MAX_ITEMS - 1; const int totitem_remain = totitem - totitem_parent; - size_t array_size = sizeof(EnumPropertyItem) * totitem_remain; + const size_t array_size = sizeof(EnumPropertyItem) * totitem_remain; /* used as but->func_argN so freeing is handled elsewhere */ EnumPropertyItem *remaining = MEM_mallocN(array_size + sizeof(EnumPropertyItem), diff --git a/source/blender/editors/interface/interface_region_popover.c b/source/blender/editors/interface/interface_region_popover.c index 43233205877..9ef68e9e187 100644 --- a/source/blender/editors/interface/interface_region_popover.c +++ b/source/blender/editors/interface/interface_region_popover.c @@ -151,7 +151,7 @@ static uiBlock *ui_block_func_POPOVER(bContext *C, uiPopupBlockHandle *handle, v UI_block_bounds_set_normal(block, block_margin); /* If menu slides out of other menu, override direction. */ - bool slideout = ui_block_is_menu(pup->but->block); + const bool slideout = ui_block_is_menu(pup->but->block); if (slideout) { UI_block_direction_set(block, UI_DIR_RIGHT); } @@ -254,7 +254,7 @@ uiPopupBlockHandle *ui_popover_panel_create( /* FIXME: maybe one day we want non panel popovers? */ { - int ui_units_x = ((PanelType *)arg)->ui_units_x; + const int ui_units_x = ((PanelType *)arg)->ui_units_x; pup->ui_size_x = U.widget_unit * (ui_units_x ? ui_units_x : UI_POPOVER_WIDTH_UNITS); } diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 947bdca4f9e..5445b098e5b 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -565,8 +565,8 @@ uiBlock *ui_popup_block_refresh(bContext *C, wmWindow *window = CTX_wm_window(C); ARegion *region = handle->region; - uiBlockCreateFunc create_func = handle->popup_create_vars.create_func; - uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func; + const uiBlockCreateFunc create_func = handle->popup_create_vars.create_func; + const uiBlockHandleCreateFunc handle_create_func = handle->popup_create_vars.handle_create_func; void *arg = handle->popup_create_vars.arg; uiBlock *block_old = region->uiblocks.first; @@ -638,7 +638,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, } if (block->flag & UI_BLOCK_RADIAL) { - int win_width = UI_SCREEN_MARGIN; + const int win_width = UI_SCREEN_MARGIN; int winx, winy; int x_offset = 0, y_offset = 0; @@ -712,7 +712,7 @@ uiBlock *ui_popup_block_refresh(bContext *C, * the same height. */ if (handle->refresh && handle->prev_block_rect.ymax > block->rect.ymax) { if (block->bounds_type != UI_BLOCK_BOUNDS_POPUP_CENTER) { - float offset = handle->prev_block_rect.ymax - block->rect.ymax; + const float offset = handle->prev_block_rect.ymax - block->rect.ymax; UI_block_translate(block, 0, offset); block->rect.ymin = handle->prev_block_rect.ymin; } diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index adb2c1c802f..0711d953ebd 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -256,8 +256,8 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr { /* thumbnail preview */ if (data->preview) { - int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols; - int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows; + const int butw = (BLI_rcti_size_x(&data->bbox) - 2 * MENU_BORDER) / data->prv_cols; + const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * MENU_BORDER) / data->prv_rows; int row, col; *r_rect = data->bbox; @@ -273,7 +273,7 @@ static void ui_searchbox_butrect(rcti *r_rect, uiSearchboxData *data, int itemnr } /* list view */ else { - int buth = (BLI_rcti_size_y(&data->bbox) - 2 * UI_POPUP_MENU_TOP) / SEARCH_ITEMS; + const int buth = (BLI_rcti_size_y(&data->bbox) - 2 * UI_POPUP_MENU_TOP) / SEARCH_ITEMS; *r_rect = data->bbox; r_rect->xmin = data->bbox.xmin + 3.0f; @@ -701,7 +701,7 @@ ARegion *ui_searchbox_create_generic(bContext *C, ARegion *butregion, uiButSearc static ARegionType type; ARegion *region; uiSearchboxData *data; - float aspect = but->block->aspect; + const float aspect = but->block->aspect; rctf rect_fl; rcti rect_i; const int margin = UI_POPUP_MARGIN; diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index c324e27dff9..afeeb4cedc9 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -221,8 +221,8 @@ static void ui_tooltip_region_draw_cb(const bContext *UNUSED(C), ARegion *region /* offset to the end of the last line */ if (field->text_suffix) { - float xofs = field->geom.x_pos; - float yofs = data->lineh * (field->geom.lines - 1); + const float xofs = field->geom.x_pos; + const float yofs = data->lineh * (field->geom.lines - 1); bbox.xmin += xofs; bbox.ymax -= yofs; @@ -535,13 +535,13 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is char *shortcut = NULL; { - uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL}; + const uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL}; UI_but_string_info_get(C, but, &op_keymap, NULL); shortcut = op_keymap.strinfo; } if (shortcut == NULL) { - ePaintMode paint_mode = BKE_paintmode_get_active_from_context(C); + 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) { const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode); @@ -768,14 +768,14 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) { - 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}; + const uiStringInfo but_label = {BUT_GET_LABEL, NULL}; + const uiStringInfo but_tip = {BUT_GET_TIP, NULL}; + const uiStringInfo enum_label = {BUT_GET_RNAENUM_LABEL, NULL}; + const uiStringInfo enum_tip = {BUT_GET_RNAENUM_TIP, NULL}; + const uiStringInfo op_keymap = {BUT_GET_OP_KEYMAP, NULL}; + const uiStringInfo prop_keymap = {BUT_GET_PROP_KEYMAP, NULL}; + const uiStringInfo rna_struct = {BUT_GET_RNASTRUCT_IDENTIFIER, NULL}; + const uiStringInfo rna_prop = {BUT_GET_RNAPROP_IDENTIFIER, NULL}; char buf[512]; @@ -884,7 +884,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } if (but->rnaprop) { - int unit_type = UI_but_unit_type_get(but); + const int unit_type = UI_but_unit_type_get(but); if (unit_type == PROP_UNIT_ROTATION) { if (RNA_property_type(but->rnaprop) == PROP_FLOAT) { diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 28279996559..b38ad9f6adb 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -184,7 +184,7 @@ void UI_fontstyle_draw_ex(const uiFontStyle *fs, } else { /* draw from boundbox center */ - float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id); + const float height = BLF_ascender(fs->uifont_id) + BLF_descender(fs->uifont_id); yofs = ceil(0.5f * (BLI_rcti_size_y(rect) - height)); } @@ -517,7 +517,8 @@ void uiStyleInit(void) /* Set default flags based on UI preferences (not render fonts) */ { - int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT | BLF_HINTING_FULL); + const int flag_disable = (BLF_MONOCHROME | BLF_HINTING_NONE | BLF_HINTING_SLIGHT | + BLF_HINTING_FULL); int flag_enable = 0; if (U.text_render & USER_TEXT_HINTING_NONE) { diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index 0708714c659..d148ff70751 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -535,7 +535,7 @@ static struct MenuSearch_Data *menu_items_from_ui_create( RNA_pointer_create(&screen->id, &RNA_Area, area, &ptr); const int space_type_ui = RNA_property_enum_get(&ptr, prop_ui_type); - int space_type_ui_index = RNA_enum_from_value(space_type_ui_items, space_type_ui); + const int space_type_ui_index = RNA_enum_from_value(space_type_ui_items, space_type_ui); if (space_type_ui_index == -1) { continue; } @@ -952,7 +952,7 @@ static void menu_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) case MENU_SEARCH_TYPE_RNA: { PointerRNA *ptr = &item->rna.ptr; PropertyRNA *prop = item->rna.prop; - int index = item->rna.index; + const int index = item->rna.index; const int prop_type = RNA_property_type(prop); bool changed = false; @@ -1131,7 +1131,7 @@ void UI_but_func_menu_search(uiBut *but) ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); /* When run from top-bar scan all areas in the current window. */ - bool include_all_areas = (area && (area->spacetype == SPACE_TOPBAR)); + const bool include_all_areas = (area && (area->spacetype == SPACE_TOPBAR)); struct MenuSearch_Data *data = menu_items_from_ui_create( C, win, area, region, include_all_areas); UI_but_func_search_set(but, diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.c index b8070ccbb25..76a6abe22cb 100644 --- a/source/blender/editors/interface/interface_template_search_operator.c +++ b/source/blender/editors/interface/interface_template_search_operator.c @@ -91,7 +91,7 @@ static void operator_search_update_fn(const bContext *C, if (index == words_len) { if (WM_operator_poll((bContext *)C, ot)) { char name[256]; - int len = strlen(ot_ui_name); + const int len = strlen(ot_ui_name); /* display name for menu, can hold hotkey */ BLI_strncpy(name, ot_ui_name, sizeof(name)); diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 0e801c8cee2..8962755ea90 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -139,7 +139,7 @@ static void template_add_button_search_menu(const bContext *C, const bool editable, const bool live_icon) { - PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop); + const PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop); ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL; const ID *idfrom = ptr->owner_id; const StructRNA *type = active_ptr.type ? active_ptr.type : RNA_property_pointer_type(ptr, prop); @@ -164,7 +164,7 @@ static void template_add_button_search_menu(const bContext *C, but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, width, height, tip); if (use_preview_icon) { - int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type); + const int icon = id ? ui_id_icon_get(C, id, use_big_size) : RNA_struct_ui_icon(type); ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); } else { @@ -183,7 +183,7 @@ static void template_add_button_search_menu(const bContext *C, but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip); if (live_icon) { - int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type); + const int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type); ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); } else { @@ -395,7 +395,7 @@ static void id_search_cb(const bContext *C, { TemplateID *template_ui = (TemplateID *)arg_template; ListBase *lb = template_ui->idlb; - int flag = RNA_property_flag(template_ui->prop); + const int flag = RNA_property_flag(template_ui->prop); /* ID listbase */ LISTBASE_FOREACH (ID *, id, lb) { @@ -415,7 +415,7 @@ static void id_search_cb_tagged(const bContext *C, { TemplateID *template_ui = (TemplateID *)arg_template; ListBase *lb = template_ui->idlb; - int flag = RNA_property_flag(template_ui->prop); + const int flag = RNA_property_flag(template_ui->prop); /* ID listbase */ LISTBASE_FOREACH (ID *, id, lb) { @@ -520,7 +520,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event) TemplateID *template_ui = (TemplateID *)arg_litem; PointerRNA idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop); ID *id = idptr.data; - int event = POINTER_AS_INT(arg_event); + const int event = POINTER_AS_INT(arg_event); const char *undo_push_label = NULL; switch (event) { @@ -1058,7 +1058,7 @@ static void template_ID(const bContext *C, RNA_int_set(but->opptr, "id_type", GS(id->name)); } else if (flag & UI_ID_OPEN) { - int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6; + const int w = id ? UI_UNIT_X : (flag & UI_ID_ADD_NEW) ? UI_UNIT_X * 3 : UI_UNIT_X * 6; if (openop) { but = uiDefIconTextButO(block, @@ -1859,13 +1859,12 @@ static void modifier_panel_id(void *md_link, char *r_name) void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C) { - ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); Object *ob = ED_object_active_context(C); ListBase *modifiers = &ob->modifiers; - bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id); + const bool panels_match = UI_panel_list_matches_data(region, modifiers, modifier_panel_id); if (!panels_match) { UI_panels_free_instanced(C, region); @@ -1883,8 +1882,7 @@ void uiTemplateModifiers(uiLayout *UNUSED(layout), bContext *C) PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); RNA_pointer_create(&ob->id, &RNA_Modifier, md, md_ptr); - Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, md_ptr); + Panel *new_panel = UI_panel_add_instanced(region, ®ion->panels, panel_idname, i, md_ptr); if (new_panel != NULL) { UI_panel_set_expand_from_list_data(C, new_panel); @@ -1969,7 +1967,7 @@ static ListBase *get_constraints(const bContext *C, bool use_bone_constraints) */ static void constraint_reorder(bContext *C, Panel *panel, int new_index) { - bool constraint_from_bone = constraint_panel_is_bone(panel); + const bool constraint_from_bone = constraint_panel_is_bone(panel); ListBase *lb = get_constraints(C, constraint_from_bone); bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); @@ -1989,7 +1987,7 @@ static void constraint_reorder(bContext *C, Panel *panel, int new_index) */ static short get_constraint_expand_flag(const bContext *C, Panel *panel) { - bool constraint_from_bone = constraint_panel_is_bone(panel); + const bool constraint_from_bone = constraint_panel_is_bone(panel); ListBase *lb = get_constraints(C, constraint_from_bone); bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); @@ -2001,7 +1999,7 @@ static short get_constraint_expand_flag(const bContext *C, Panel *panel) */ static void set_constraint_expand_flag(const bContext *C, Panel *panel, short expand_flag) { - bool constraint_from_bone = constraint_panel_is_bone(panel); + const bool constraint_from_bone = constraint_panel_is_bone(panel); ListBase *lb = get_constraints(C, constraint_from_bone); bConstraint *con = BLI_findlink(lb, panel->runtime.list_index); @@ -2037,7 +2035,6 @@ static void bone_constraint_panel_id(void *md_link, char *r_name) */ void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_constraints) { - ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); Object *ob = ED_object_active_context(C); @@ -2047,7 +2044,7 @@ void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_ uiListPanelIDFromDataFunc panel_id_func = use_bone_constraints ? bone_constraint_panel_id : object_constraint_panel_id; - bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func); + const bool panels_match = UI_panel_list_matches_data(region, constraints, panel_id_func); if (!panels_match) { UI_panels_free_instanced(C, region); @@ -2060,8 +2057,7 @@ void uiTemplateConstraints(uiLayout *UNUSED(layout), bContext *C, bool use_bone_ PointerRNA *con_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); RNA_pointer_create(&ob->id, &RNA_Constraint, con, con_ptr); - Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, con_ptr); + Panel *new_panel = UI_panel_add_instanced(region, ®ion->panels, panel_idname, i, con_ptr); if (new_panel) { /* Set the list panel functionality function pointers since we don't do it with python. */ @@ -2119,12 +2115,12 @@ static void gpencil_modifier_panel_id(void *md_link, char *r_name) void uiTemplateGpencilModifiers(uiLayout *UNUSED(layout), bContext *C) { - ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); Object *ob = ED_object_active_context(C); ListBase *modifiers = &ob->greasepencil_modifiers; - bool panels_match = UI_panel_list_matches_data(region, modifiers, gpencil_modifier_panel_id); + const bool panels_match = UI_panel_list_matches_data( + region, modifiers, gpencil_modifier_panel_id); if (!panels_match) { UI_panels_free_instanced(C, region); @@ -2142,8 +2138,7 @@ void uiTemplateGpencilModifiers(uiLayout *UNUSED(layout), bContext *C) PointerRNA *md_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, md_ptr); - Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, md_ptr); + Panel *new_panel = UI_panel_add_instanced(region, ®ion->panels, panel_idname, i, md_ptr); if (new_panel != NULL) { UI_panel_set_expand_from_list_data(C, new_panel); @@ -2208,12 +2203,11 @@ static void shaderfx_panel_id(void *fx_v, char *r_idname) */ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C) { - ScrArea *sa = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); Object *ob = ED_object_active_context(C); ListBase *shaderfx = &ob->shader_fx; - bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id); + const bool panels_match = UI_panel_list_matches_data(region, shaderfx, shaderfx_panel_id); if (!panels_match) { UI_panels_free_instanced(C, region); @@ -2226,8 +2220,7 @@ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C) PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata"); RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr); - Panel *new_panel = UI_panel_add_instanced( - sa, region, ®ion->panels, panel_idname, i, fx_ptr); + Panel *new_panel = UI_panel_add_instanced(region, ®ion->panels, panel_idname, i, fx_ptr); if (new_panel != NULL) { UI_panel_set_expand_from_list_data(C, new_panel); @@ -2310,7 +2303,7 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single( eAutoPropButsReturn return_info = 0; if (!op->properties) { - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; op->properties = IDP_New(IDP_GROUP, &val, "wmOperatorProperties"); } @@ -3035,8 +3028,8 @@ static void colorband_distribute_cb(bContext *C, ColorBand *coba, bool evenly) { if (coba->tot > 1) { int a; - int tot = evenly ? coba->tot - 1 : coba->tot; - float gap = 1.0f / tot; + const int tot = evenly ? coba->tot - 1 : coba->tot; + const float gap = 1.0f / tot; float pos = 0.0f; for (a = 0; a < coba->tot; a++) { coba->data[a].pos = pos; @@ -3218,9 +3211,9 @@ static void colorband_buttons_layout(uiLayout *layout, { uiLayout *row, *split, *subsplit; uiBut *bt; - float unit = BLI_rctf_size_x(butr) / 14.0f; - float xs = butr->xmin; - float ys = butr->ymin; + const float unit = BLI_rctf_size_x(butr) / 14.0f; + const float xs = butr->xmin; + const float ys = butr->ymin; PointerRNA ptr; RNA_pointer_create(cb->ptr.owner_id, &RNA_ColorRamp, coba, &ptr); @@ -3893,7 +3886,7 @@ static uiBlock *curvemap_clipping_func(bContext *C, ARegion *region, void *cumap CurveMapping *cumap = cumap_v; uiBlock *block; uiBut *bt; - float width = 8 * UI_UNIT_X; + const float width = 8 * UI_UNIT_X; block = UI_block_begin(C, region, __func__, UI_EMBOSS); UI_block_flag_enable(block, UI_BLOCK_KEEP_OPEN | UI_BLOCK_MOVEMOUSE_QUIT); @@ -4235,7 +4228,7 @@ static void curvemap_buttons_layout(uiLayout *layout, uiLayout *row, *sub, *split; uiBlock *block; uiBut *bt; - float dx = UI_UNIT_X; + const float dx = UI_UNIT_X; int icon, size; int bg = -1, i; @@ -4708,7 +4701,7 @@ static uiBlock *CurveProfile_tools_func(bContext *C, ARegion *region, CurveProfi { uiBlock *block; short yco = 0; - short menuwidth = 10 * UI_UNIT_X; + const short menuwidth = 10 * UI_UNIT_X; block = UI_block_begin(C, region, __func__, UI_EMBOSS); UI_block_func_butmenu_set(block, CurveProfile_tools_dofunc, profile); @@ -5417,7 +5410,7 @@ void uiTemplatePalette(uiLayout *layout, uiBut *but = NULL; int row_cols = 0, col_id = 0; - int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1); + const int cols_per_row = MAX2(uiLayoutGetWidth(layout) / UI_UNIT_X, 1); if (!prop) { RNA_warning("property not found: %s.%s", RNA_struct_identifier(ptr->type), propname); @@ -5564,7 +5557,7 @@ void uiTemplateCryptoPicker(uiLayout *layout, PointerRNA *ptr, const char *propn static void handle_layer_buttons(bContext *C, void *arg1, void *arg2) { uiBut *but = arg1; - int cur = POINTER_AS_INT(arg2); + const int cur = POINTER_AS_INT(arg2); wmWindow *win = CTX_wm_window(C); int i, tot, shift = win->eventstate->shift; @@ -5599,7 +5592,7 @@ void uiTemplateLayers(uiLayout *layout, PropertyRNA *prop, *used_prop = NULL; int groups, cols, layers; int group, col, layer, row; - int cols_per_group = 5; + const int cols_per_group = 5; prop = RNA_struct_find_property(ptr, propname); if (!prop) { @@ -5647,7 +5640,7 @@ void uiTemplateLayers(uiLayout *layout, /* add layers as toggle buts */ for (col = 0; (col < cols_per_group) && (layer < layers); col++, layer++) { int icon = 0; - int butlay = 1 << layer; + const int butlay = 1 << layer; if (active_layer & butlay) { icon = ICON_LAYER_ACTIVE; @@ -5762,7 +5755,7 @@ static void uilist_filter_items_default(struct uiList *ui_list, const bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0; const bool order_by_name = (ui_list->filter_sort_flag & UILST_FLT_SORT_MASK) == UILST_FLT_SORT_ALPHA; - int len = RNA_property_collection_length(dataptr, prop); + const int len = RNA_property_collection_length(dataptr, prop); dyn_data->items_shown = dyn_data->items_len = len; @@ -5774,7 +5767,7 @@ static void uilist_filter_items_default(struct uiList *ui_list, names = MEM_callocN(sizeof(StringCmp) * len, "StringCmp"); } if (filter_raw[0]) { - size_t slen = strlen(filter_raw); + const size_t slen = strlen(filter_raw); dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, "items_filter_flags"); dyn_data->items_shown = 0; @@ -6212,8 +6205,8 @@ void uiTemplateList(uiLayout *layout, for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { PointerRNA *itemptr = &items_ptr[i].item; void *dyntip_data; - int org_i = items_ptr[i].org_idx; - int flt_flag = items_ptr[i].flt_flag; + const int org_i = items_ptr[i].org_idx; + const int flt_flag = items_ptr[i].flt_flag; subblock = uiLayoutGetBlock(col); overlap = uiLayoutOverlap(col); @@ -6299,7 +6292,7 @@ void uiTemplateList(uiLayout *layout, if ((dataptr->data && prop) && (dyn_data->items_shown > 0) && (activei >= 0) && (activei < dyn_data->items_shown)) { PointerRNA *itemptr = &items_ptr[activei].item; - int org_i = items_ptr[activei].org_idx; + const int org_i = items_ptr[activei].org_idx; icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false); if (icon == ICON_DOT) { @@ -6349,8 +6342,8 @@ void uiTemplateList(uiLayout *layout, /* create list items */ for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) { PointerRNA *itemptr = &items_ptr[i].item; - int org_i = items_ptr[i].org_idx; - int flt_flag = items_ptr[i].flt_flag; + const int org_i = items_ptr[i].org_idx; + const int flt_flag = items_ptr[i].flt_flag; /* create button */ if (!(i % columns)) { @@ -6738,7 +6731,7 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C) if (owner) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - bool active = !(G.is_break || WM_jobs_is_stopped(wm, owner)); + const bool active = !(G.is_break || WM_jobs_is_stopped(wm, owner)); uiLayout *row = uiLayoutRow(layout, false); block = uiLayoutGetBlock(row); @@ -7086,7 +7079,7 @@ bool uiTemplateEventFromKeymapItem(struct uiLayout *layout, #ifdef WITH_HEADLESS int icon = 0; #else - int icon = UI_icon_from_keymap_item(kmi, icon_mod); + const int icon = UI_icon_from_keymap_item(kmi, icon_mod); #endif if (icon != 0) { for (int j = 0; j < ARRAY_SIZE(icon_mod) && icon_mod[j]; j++) { diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index f44987ac1d2..6be281cd050 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -249,7 +249,7 @@ uiBut *uiDefAutoButR(uiBlock *block, break; case PROP_POINTER: { if (icon == 0) { - PointerRNA pptr = RNA_property_pointer_get(ptr, prop); + const PointerRNA pptr = RNA_property_pointer_get(ptr, prop); icon = RNA_struct_ui_icon(pptr.type ? pptr.type : RNA_property_pointer_type(ptr, prop)); } if (icon == ICON_DOT) { @@ -436,7 +436,7 @@ void ui_rna_collection_search_update_fn(const struct bContext *C, int name_prefix_offset = 0; int iconid = ICON_NONE; bool has_sep_char = false; - bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); + const bool is_id = itemptr.type && RNA_struct_is_ID(itemptr.type); if (is_id) { iconid = ui_id_icon_get(C, itemptr.data, false); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index c1801290152..ab0f6d90caa 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -475,7 +475,7 @@ GPUBatch *ui_batch_roundbox_shadow_get(void) uint32_t last_data; GPUVertBufRaw vflag_step; GPUVertBuf *vbo = GPU_vertbuf_create_with_format(vflag_format()); - int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX; + const int vcount = (WIDGET_SIZE_MAX + 1) * 2 + 2 + WIDGET_SIZE_MAX; GPU_vertbuf_data_alloc(vbo, vcount); GPU_vertbuf_attr_get_raw_data(vbo, g_ui_batch_cache.vflag_id, &vflag_step); @@ -528,7 +528,8 @@ void UI_draw_anti_tria( GPU_blend(GPU_BLEND_ALPHA); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4fv(draw_color); @@ -552,12 +553,12 @@ void UI_draw_anti_tria( void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) { if (dir == 'h') { - float half = 0.5f * BLI_rctf_size_y(rect); + const float half = 0.5f * BLI_rctf_size_y(rect); UI_draw_anti_tria( rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color); } else { - float half = 0.5f * BLI_rctf_size_x(rect); + const float half = 0.5f * BLI_rctf_size_x(rect); UI_draw_anti_tria( rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color); } @@ -572,7 +573,8 @@ void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4]) GPU_blend(GPU_BLEND_ALPHA); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4fv(draw_color); @@ -706,14 +708,14 @@ static void round_box__edges( uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad, float radi) { float vec[WIDGET_CURVE_RESOLU][2], veci[WIDGET_CURVE_RESOLU][2]; - float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax; - float minxi = minx + U.pixelsize; /* boundbox inner */ - float maxxi = maxx - U.pixelsize; - float minyi = miny + U.pixelsize; - float maxyi = maxy - U.pixelsize; + const float minx = rect->xmin, miny = rect->ymin, maxx = rect->xmax, maxy = rect->ymax; + const float minxi = minx + U.pixelsize; /* boundbox inner */ + const float maxxi = maxx - U.pixelsize; + const float minyi = miny + U.pixelsize; + const float maxyi = maxy - U.pixelsize; /* for uv, can divide by zero */ - float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f; - float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f; + const float facxi = (maxxi != minxi) ? 1.0f / (maxxi - minxi) : 0.0f; + const float facyi = (maxyi != minyi) ? 1.0f / (maxyi - minyi) : 0.0f; int a, tot = 0, minsize; const int hnum = ((roundboxalign & (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT)) == (UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT) || @@ -1012,8 +1014,8 @@ static void widget_draw_vertex_buffer(uint pos, static void shape_preset_trias_from_rect_menu(uiWidgetTrias *tria, const rcti *rect) { - float width = BLI_rcti_size_x(rect); - float height = BLI_rcti_size_y(rect); + const float width = BLI_rcti_size_x(rect); + const float height = BLI_rcti_size_y(rect); float centx, centy, size; tria->type = ROUNDBOX_TRIA_MENU; @@ -1096,7 +1098,8 @@ static void widgetbase_outline(uiWidgetBase *wtb, uint pos) float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2]; /* + 2 because the last pair is wrapped */ widget_verts_to_triangle_strip(wtb, wtb->totvert, triangle_strip); - widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2); + widget_draw_vertex_buffer( + pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, NULL, wtb->totvert * 2 + 2); } static void widgetbase_set_uniform_alpha_discard(uiWidgetBase *wtb, @@ -1119,7 +1122,7 @@ static void widgetbase_set_uniform_alpha_check(uiWidgetBase *wtb, const bool alp static void widgetbase_set_uniform_discard_factor(uiWidgetBase *wtb, const float discard_factor) { - bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f; + const bool alpha_check = wtb->uniform_params.alpha_discard < 0.0f; widgetbase_set_uniform_alpha_discard(wtb, alpha_check, discard_factor); } @@ -1343,8 +1346,8 @@ static void widget_draw_preview(BIFIconID icon, float alpha, const rcti *rect) size -= PREVIEW_PAD * 2; /* padding */ if (size > 0) { - int x = rect->xmin + w / 2 - size / 2; - int y = rect->ymin + h / 2 - size / 2; + const int x = rect->xmin + w / 2 - size / 2; + const int y = rect->ymin + h / 2 - size / 2; UI_icon_draw_preview(x, y, icon, 1.0f, alpha, size); } @@ -1405,7 +1408,7 @@ static void widget_draw_icon( GPU_blend(GPU_BLEND_ALPHA); if (icon && icon != ICON_BLANK1) { - float ofs = 1.0f / aspect; + const float ofs = 1.0f / aspect; if (but->drawflag & UI_BUT_ICON_LEFT) { /* special case - icon_only pie buttons */ @@ -1433,7 +1436,7 @@ static void widget_draw_icon( /* Get theme color. */ uchar color[4] = {mono_color[0], mono_color[1], mono_color[2], mono_color[3]}; - bool has_theme = UI_icon_get_theme_color(icon, color); + const bool has_theme = UI_icon_get_theme_color(icon, color); /* to indicate draggable */ if (but->dragpoin && (but->flag & UI_ACTIVE)) { @@ -1485,7 +1488,7 @@ static void widget_draw_submenu_tria(const uiBut *but, static void ui_text_clip_give_prev_off(uiBut *but, const char *str) { const char *prev_utf8 = BLI_str_find_prev_char_utf8(str, str + but->ofs); - int bytes = str + but->ofs - prev_utf8; + const int bytes = str + but->ofs - prev_utf8; but->ofs -= bytes; } @@ -1493,7 +1496,7 @@ static void ui_text_clip_give_prev_off(uiBut *but, const char *str) static void ui_text_clip_give_next_off(uiBut *but, const char *str) { const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, NULL); - int bytes = next_utf8 - (str + but->ofs); + const int bytes = next_utf8 - (str + but->ofs); but->ofs += bytes; } @@ -1822,7 +1825,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons /* chop off the leading text, starting from the right */ while (but->strwidth > okwidth && cp2 > but->drawstr) { const char *prev_utf8 = BLI_str_find_prev_char_utf8(but->drawstr, cp2); - int bytes = cp2 - prev_utf8; + const int bytes = cp2 - prev_utf8; /* shift the text after and including cp2 back by 1 char, * +1 to include null terminator */ @@ -2285,7 +2288,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, rcti *rect) { const bool show_menu_icon = ui_but_draw_menu_icon(but); - float alpha = (float)wcol->text[3] / 255.0f; + const float alpha = (float)wcol->text[3] / 255.0f; char password_str[UI_MAX_DRAW_STR]; bool no_text_padding = but->drawflag & UI_BUT_NO_TEXT_PADDING; @@ -2353,7 +2356,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, #endif const BIFIconID icon = ui_but_icon(but); - int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT; + const int icon_size_init = is_tool ? ICON_DEFAULT_HEIGHT_TOOLBAR : ICON_DEFAULT_HEIGHT; const float icon_size = icon_size_init / (but->block->aspect * U.inv_dpi_fac); const float icon_padding = 2 * UI_DPI_FAC; @@ -2398,7 +2401,7 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, rect->xmin += text_padding; } else if (but->flag & UI_BUT_DRAG_MULTI) { - bool text_is_edited = ui_but_drag_multi_edit_get(but) != NULL; + const bool text_is_edited = ui_but_drag_multi_edit_get(but) != NULL; if (text_is_edited || (but->drawflag & UI_BUT_TEXT_LEFT)) { rect->xmin += text_padding; } @@ -2486,7 +2489,7 @@ static void ui_widget_color_disabled(uiWidgetType *wt) static void widget_active_color(uiWidgetColors *wcol) { - bool dark = (rgb_to_grayscale_byte(wcol->text) > rgb_to_grayscale_byte(wcol->inner)); + const bool dark = (rgb_to_grayscale_byte(wcol->text) > rgb_to_grayscale_byte(wcol->inner)); color_mul_hsl_v3(wcol->inner, 1.0f, 1.15f, dark ? 1.2f : 1.1f); color_mul_hsl_v3(wcol->outline, 1.0f, 1.15f, 1.15f); color_mul_hsl_v3(wcol->text, 1.0f, 1.15f, dark ? 1.25f : 0.8f); @@ -2745,12 +2748,13 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r /* we draw a number of increasing size alpha quad strips */ alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout; - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); for (step = 1; step <= (int)radout; step++) { - float expfac = sqrtf(step / radout); + const float expfac = sqrtf(step / radout); round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, (float)step); @@ -2758,7 +2762,7 @@ static void widget_softshadow(const rcti *rect, int roundboxalign, const float r widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip); - widget_draw_vertex_buffer(pos, 0, GL_TRIANGLE_STRIP, triangle_strip, NULL, totvert * 2); + widget_draw_vertex_buffer(pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, NULL, totvert * 2); } immUnbindProgram(); @@ -2797,7 +2801,8 @@ static void widget_menu_back(uiWidgetColors *wcol, rcti *rect, int flag, int dir static void ui_hsv_cursor(float x, float y) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -2860,11 +2865,11 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const const float radstep = 2.0f * (float)M_PI / (float)tot; const float centx = BLI_rcti_cent_x_fl(rect); const float centy = BLI_rcti_cent_y_fl(rect); - float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f; + const float radius = (float)min_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)) / 2.0f; ColorPicker *cpicker = but->custom_data; float rgb[3], hsv[3], rgb_center[3]; - bool is_color_gamma = ui_but_is_color_gamma(but); + const bool is_color_gamma = ui_but_is_color_gamma(but); /* Initialize for compatibility. */ copy_v3_v3(hsv, cpicker->color_data); @@ -2898,7 +2903,7 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); @@ -2908,8 +2913,8 @@ static void ui_draw_but_HSVCIRCLE(uiBut *but, const uiWidgetColors *wcol, const float ang = 0.0f; for (int a = 0; a <= tot; a++, ang += radstep) { - float si = sinf(ang); - float co = cosf(ang); + const float si = sinf(ang); + const float co = cosf(ang); float hsv_ang[3]; float rgb_ang[3]; @@ -2974,7 +2979,7 @@ void ui_draw_gradient(const rcti *rect, const int steps = 48; const float color_step = 1.0f / steps; int a; - float h = hsv[0], s = hsv[1], v = hsv[2]; + const float h = hsv[0], s = hsv[1], v = hsv[2]; float dx, dy, sx1, sx2, sy; float col0[4][3]; /* left half, rect bottom to top */ float col1[4][3]; /* right half, rect bottom to top */ @@ -3029,8 +3034,8 @@ void ui_draw_gradient(const rcti *rect, /* old below */ GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_SMOOTH_COLOR); immBegin(GPU_PRIM_TRIS, steps * 3 * 6); @@ -3192,7 +3197,8 @@ static void ui_draw_but_HSVCUBE(uiBut *but, const rcti *rect) ui_hsv_cursor(x, y); /* outline */ - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3ub(0, 0, 0); imm_draw_box_wire_2d(pos, (rect->xmin), (rect->ymin), (rect->xmax), (rect->ymax)); @@ -3223,7 +3229,7 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) /* map v from property range to [0,1] */ if (hsv_but->gradient_type == UI_GRAD_V_ALT) { - float min = but->softmin, max = but->softmax; + const float min = but->softmin, max = but->softmax; v = (v - min) / (max - min); } @@ -3258,7 +3264,7 @@ static void ui_draw_but_HSV_v(uiBut *but, const rcti *rect) /** Separator for menus. */ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) { - int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1; + const int y = rect->ymin + BLI_rcti_size_y(rect) / 2 - 1; const uchar col[4] = { wcol->text[0], wcol->text[1], @@ -3266,7 +3272,8 @@ static void ui_draw_separator(const rcti *rect, const uiWidgetColors *wcol) 30, }; - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); GPU_blend(GPU_BLEND_ALPHA); @@ -3618,8 +3625,8 @@ static void widget_progressbar( widget_init(&wtb_bar); /* round corners */ - float value = but_progressbar->progress; - float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog); + const float value = but_progressbar->progress; + const float offs = wcol->roundness * BLI_rcti_size_y(&rect_prog); float w = value * BLI_rcti_size_x(&rect_prog); /* ensure minimium size */ @@ -3646,7 +3653,7 @@ static void widget_nodesocket( uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) { uiWidgetBase wtb; - int radi = 5; + const int radi = 5; uchar old_inner[3], old_outline[3]; widget_init(&wtb); @@ -3662,8 +3669,8 @@ static void widget_nodesocket( wcol->outline[2] = 0; wcol->outline[3] = 150; - int cent_x = BLI_rcti_cent_x(rect); - int cent_y = BLI_rcti_cent_y(rect); + const int cent_x = BLI_rcti_cent_x(rect); + const int cent_y = BLI_rcti_cent_y(rect); rect->xmin = cent_x - radi; rect->xmax = cent_x + radi; rect->ymin = cent_y - radi; @@ -3711,7 +3718,7 @@ static void widget_numslider( rect1 = *rect; float factor, factor_ui; float factor_discard = 1.0f; /* No discard. */ - float value = (float)ui_but_value_get(but); + const float value = (float)ui_but_value_get(but); if (but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PERCENTAGE)) { factor = value / but->softmax; @@ -3720,7 +3727,7 @@ static void widget_numslider( factor = (value - but->softmin) / (but->softmax - but->softmin); } - float width = (float)BLI_rcti_size_x(rect); + const float width = (float)BLI_rcti_size_x(rect); factor_ui = factor * width; if (factor_ui <= offs) { @@ -3828,8 +3835,8 @@ static void widget_swatch( widgetbase_draw_ex(&wtb, wcol, show_alpha_checkers); if (color_but->is_pallete_color && ((Palette *)but->rnapoin.owner_id)->active_color == color_but->palette_color_index) { - float width = rect->xmax - rect->xmin; - float height = rect->ymax - rect->ymin; + const float width = rect->xmax - rect->xmin; + const float height = rect->ymax - rect->ymin; /* find color luminance and change it slightly */ float bw = rgb_to_grayscale(col); @@ -3840,7 +3847,8 @@ static void widget_swatch( UI_widgetbase_draw_cache_flush(); GPU_blend(GPU_BLEND_NONE); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor3f(bw, bw, bw); @@ -3968,7 +3976,7 @@ static void widget_menu_radial_itembut( { uiWidgetBase wtb; float rad; - float fac = but->block->pie_data.alphafac; + const float fac = but->block->pie_data.alphafac; widget_init(&wtb); @@ -4010,7 +4018,7 @@ static void widget_optionbut(uiWidgetColors *wcol, int state, int UNUSED(roundboxalign)) { - bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET); + const bool text_before_widget = (state & UI_STATE_TEXT_BEFORE_WIDGET); uiWidgetBase wtb; rcti recttemp = *rect; float rad; @@ -4232,7 +4240,8 @@ static void widget_draw_extra_mask(const bContext *C, uiBut *but, uiWidgetType * but->block->drawextra( C, but->poin, but->block->drawextra_arg1, but->block->drawextra_arg2, rect); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* make mask to draw over image */ @@ -4474,7 +4483,7 @@ static int widget_roundbox_set(uiBut *but, rcti *rect) /* align with open menu */ if (but->active && (but->type != UI_BTYPE_POPOVER) && !ui_but_menu_draw_as_popover(but)) { - int direction = ui_but_menu_direction(but); + const int direction = ui_but_menu_direction(but); if (direction == UI_DIR_UP) { roundbox &= ~(UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT); @@ -4922,7 +4931,8 @@ static void ui_draw_popover_back_impl(const uiWidgetColors *wcol, /* Draw popover arrow (top/bottom) */ if (ELEM(direction, UI_DIR_UP, UI_DIR_DOWN)) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); const bool is_down = (direction == UI_DIR_DOWN); @@ -5043,18 +5053,18 @@ static void draw_disk_shaded(float start, void ui_draw_pie_center(uiBlock *block) { bTheme *btheme = UI_GetTheme(); - float cx = block->pie_data.pie_center_spawned[0]; - float cy = block->pie_data.pie_center_spawned[1]; + const float cx = block->pie_data.pie_center_spawned[0]; + const float cy = block->pie_data.pie_center_spawned[1]; float *pie_dir = block->pie_data.pie_dir; - float pie_radius_internal = U.dpi_fac * U.pie_menu_threshold; - float pie_radius_external = U.dpi_fac * (U.pie_menu_threshold + 7.0f); + const float pie_radius_internal = U.dpi_fac * U.pie_menu_threshold; + const float pie_radius_external = U.dpi_fac * (U.pie_menu_threshold + 7.0f); - int subd = 40; + const int subd = 40; - float angle = atan2f(pie_dir[1], pie_dir[0]); - float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4; + const float angle = atan2f(pie_dir[1], pie_dir[0]); + const float range = (block->pie_data.flags & UI_PIE_DEGREES_RANGE_LARGE) ? M_PI_2 : M_PI_4; GPU_matrix_push(); GPU_matrix_translate_2f(cx, cy); @@ -5117,7 +5127,7 @@ void ui_draw_pie_center(uiBlock *block) } GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); immUniformColor4ubv(btheme->tui.wcol_pie_menu.outline); @@ -5128,8 +5138,9 @@ void ui_draw_pie_center(uiBlock *block) if (U.pie_menu_confirm > 0 && !(block->pie_data.flags & (UI_PIE_INVALID_DIR | UI_PIE_CLICK_STYLE))) { - float pie_confirm_radius = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm); - float pie_confirm_external = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm + 7.0f); + const float pie_confirm_radius = U.dpi_fac * (pie_radius_internal + U.pie_menu_confirm); + const float pie_confirm_external = U.dpi_fac * + (pie_radius_internal + U.pie_menu_confirm + 7.0f); const uchar col[4] = {UNPACK3(btheme->tui.wcol_pie_menu.text_sel), 64}; draw_disk_shaded(angle - range / 2.0f, @@ -5212,7 +5223,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, int *r_xmax) { uiWidgetType *wt = widget_type(UI_WTYPE_MENU_ITEM); - rcti _rect = *rect; + const rcti _rect = *rect; char *cpoin = NULL; wt->state(wt, state, 0); @@ -5282,8 +5293,8 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, if (iconid) { float height, aspect; - int xs = rect->xmin + 0.2f * UI_UNIT_X; - int ys = rect->ymin + 0.1f * BLI_rcti_size_y(rect); + const int xs = rect->xmin + 0.2f * UI_UNIT_X; + const int ys = rect->ymin + 0.1f * BLI_rcti_size_y(rect); height = ICON_SIZE_FROM_BUTRECT(rect); aspect = ICON_DEFAULT_HEIGHT / height; diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 84fe3e13426..87474369e8d 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -774,9 +774,6 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) cp = ts->metadatatext; break; - case TH_UV_OTHERS: - cp = ts->uv_others; - break; case TH_UV_SHADOW: cp = ts->uv_shadow; break; @@ -1474,13 +1471,6 @@ void UI_ThemeClearColor(int colorid) GPU_clear_color(col[0], col[1], col[2], 1.0f); } -void UI_ThemeClearColorAlpha(int colorid, float alpha) -{ - float col[3]; - UI_GetThemeColor3fv(colorid, col); - GPU_clear_color(col[0], col[1], col[2], alpha); -} - int UI_ThemeMenuShadowWidth(void) { bTheme *btheme = UI_GetTheme(); diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index d4f81a89bc3..7651989c2df 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -351,7 +351,7 @@ void UI_view2d_region_reinit(View2D *v2d, short type, int winx, int winy) v2d->scroll |= (V2D_SCROLL_HORIZONTAL_HIDE | V2D_SCROLL_VERTICAL_HIDE); if (do_init) { - float panelzoom = (style) ? style->panelzoom : 1.0f; + const float panelzoom = (style) ? style->panelzoom : 1.0f; v2d->tot.xmin = 0.0f; v2d->tot.xmax = winx; @@ -566,7 +566,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize) * - width is not adjusted for changed ratios here. */ if (winx < v2d->oldwinx) { - float temp = v2d->oldwinx - winx; + const float temp = v2d->oldwinx - winx; cur->xmin -= temp; cur->xmax -= temp; @@ -588,7 +588,7 @@ static void ui_view2d_curRect_validate_resize(View2D *v2d, bool resize) */ if (winy < v2d->oldwiny) { - float temp = v2d->oldwiny - winy; + const float temp = v2d->oldwiny - winy; if (v2d->align & V2D_ALIGN_NO_NEG_Y) { cur->ymin -= temp; @@ -1121,14 +1121,14 @@ static void view2d_map_cur_using_mask(const View2D *v2d, rctf *r_curmasked) *r_curmasked = v2d->cur; if (view2d_scroll_mapped(v2d->scroll)) { - float sizex = BLI_rcti_size_x(&v2d->mask); - float sizey = BLI_rcti_size_y(&v2d->mask); + const float sizex = BLI_rcti_size_x(&v2d->mask); + const float sizey = BLI_rcti_size_y(&v2d->mask); /* prevent tiny or narrow regions to get * invalid coordinates - mask can get negative even... */ if (sizex > 0.0f && sizey > 0.0f) { - float dx = BLI_rctf_size_x(&v2d->cur) / (sizex + 1); - float dy = BLI_rctf_size_y(&v2d->cur) / (sizey + 1); + const float dx = BLI_rctf_size_x(&v2d->cur) / (sizex + 1); + const float dy = BLI_rctf_size_y(&v2d->cur) / (sizey + 1); if (v2d->mask.xmin != 0) { r_curmasked->xmin -= dx * (float)v2d->mask.xmin; @@ -1225,8 +1225,8 @@ void UI_view2d_view_orthoSpecial(ARegion *region, View2D *v2d, const bool xaxis) void UI_view2d_view_restore(const bContext *C) { ARegion *region = CTX_wm_region(C); - int width = BLI_rcti_size_x(®ion->winrct) + 1; - int height = BLI_rcti_size_y(®ion->winrct) + 1; + const int width = BLI_rcti_size_x(®ion->winrct) + 1; + const int height = BLI_rcti_size_y(®ion->winrct) + 1; wmOrtho2(0.0f, (float)width, 0.0f, (float)height); GPU_matrix_identity_set(); @@ -1278,8 +1278,8 @@ void UI_view2d_constant_grid_draw(const View2D *v2d, float step) if (count_x > 0 || count_y > 0) { GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); float theme_color[3]; UI_GetThemeColorShade3fv(TH_BACK, -10, theme_color); @@ -1331,7 +1331,7 @@ void UI_view2d_multi_grid_draw( vertex_count += 2 * ((int)((v2d->cur.ymax - v2d->cur.ymin) / lstep) + 1); GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); uint color = GPU_vertformat_attr_add( format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); @@ -1428,7 +1428,7 @@ void UI_view2d_scrollers_calc(View2D *v2d, { rcti vert, hor; float fac1, fac2, totsize, scrollsize; - int scroll = view2d_scroll_mapped(v2d->scroll); + const int scroll = view2d_scroll_mapped(v2d->scroll); int smaller; /* Always update before drawing (for dynamically sized scrollers). */ @@ -1910,7 +1910,7 @@ View2D *UI_view2d_fromcontext_rwin(const bContext *C) * disabled. */ void UI_view2d_scroller_size_get(const View2D *v2d, float *r_x, float *r_y) { - int scroll = view2d_scroll_mapped(v2d->scroll); + const int scroll = view2d_scroll_mapped(v2d->scroll); if (r_x) { if (scroll & V2D_SCROLL_VERTICAL) { @@ -2128,7 +2128,7 @@ void UI_view2d_text_cache_add( BLI_assert(str_len == strlen(str)); if (UI_view2d_view_to_region_clip(v2d, x, y, &mval[0], &mval[1])) { - int alloc_len = str_len + 1; + const int alloc_len = str_len + 1; View2DString *v2s; if (g_v2d_strings_arena == NULL) { @@ -2159,7 +2159,7 @@ void UI_view2d_text_cache_add_rectf( BLI_assert(str_len == strlen(str)); if (UI_view2d_view_to_region_rcti_clip(v2d, rect_view, &rect)) { - int alloc_len = str_len + 1; + const int alloc_len = str_len + 1; View2DString *v2s; if (g_v2d_strings_arena == NULL) { diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c index 54b25939baf..5801b7cdbdb 100644 --- a/source/blender/editors/interface/view2d_draw.c +++ b/source/blender/editors/interface/view2d_draw.c @@ -67,10 +67,10 @@ static float select_major_distance(const float *possible_distances, return possible_distances[0]; } - float pixels_per_view_unit = pixel_width / view_width; + const float pixels_per_view_unit = pixel_width / view_width; for (uint i = 0; i < amount; i++) { - float distance = possible_distances[i]; + const float distance = possible_distances[i]; if (pixels_per_view_unit * distance >= MIN_MAJOR_LINE_DISTANCE) { return distance; } @@ -111,7 +111,7 @@ static float view2d_major_step_y__continuous(const View2D *v2d) static float view2d_major_step_x__time(const View2D *v2d, const Scene *scene) { - double fps = FPS; + const double fps = FPS; float *possible_distances = NULL; BLI_array_staticdeclare(possible_distances, 32); @@ -207,7 +207,7 @@ static void draw_parallel_lines(const ParallelLinesSet *lines, } GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); if (U.pixelsize > 1.0f) { float viewport[4]; @@ -227,14 +227,14 @@ static void draw_parallel_lines(const ParallelLinesSet *lines, if (direction == 'v') { for (uint i = 0; i < steps; i++) { - float xpos = first + i * lines->distance; + const float xpos = first + i * lines->distance; immVertex2f(pos, xpos, rect->ymin); immVertex2f(pos, xpos, rect->ymax); } } else { for (uint i = 0; i < steps; i++) { - float ypos = first + i * lines->distance; + const float ypos = first + i * lines->distance; immVertex2f(pos, rect->xmin, ypos); immVertex2f(pos, rect->xmax, ypos); } @@ -322,16 +322,16 @@ static void draw_horizontal_scale_indicators(const ARegion *region, BLF_batch_draw_begin(); - float ypos = rect->ymin + 4 * UI_DPI_FAC; - float xmin = rect->xmin; - float xmax = rect->xmax; + const float ypos = rect->ymin + 4 * UI_DPI_FAC; + const float xmin = rect->xmin; + const float xmax = rect->xmax; for (uint i = 0; i < steps; i++) { - float xpos_view = start + i * distance; - float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view); + const float xpos_view = start + i * distance; + const float xpos_region = UI_view2d_view_to_region_x(v2d, xpos_view); char text[32]; to_string(to_string_data, xpos_view, distance, sizeof(text), text); - float text_width = BLF_width(font_id, text, strlen(text)); + const float text_width = BLF_width(font_id, text, strlen(text)); if (xpos_region - text_width / 2.0f >= xmin && xpos_region + text_width / 2.0f <= xmax) { BLF_draw_default_ascii(xpos_region - text_width / 2.0f, ypos, 0.0f, text, sizeof(text)); @@ -384,16 +384,16 @@ static void draw_vertical_scale_indicators(const ARegion *region, BLF_batch_draw_begin(); - float xpos = rect->xmax - 2.0f * UI_DPI_FAC; - float ymin = rect->ymin; - float ymax = rect->ymax; + const float xpos = rect->xmax - 2.0f * UI_DPI_FAC; + const float ymin = rect->ymin; + const float ymax = rect->ymax; for (uint i = 0; i < steps; i++) { - float ypos_view = start + i * distance; - float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset); + const float ypos_view = start + i * distance; + const float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset); char text[32]; to_string(to_string_data, ypos_view, distance, sizeof(text), text); - float text_width = BLF_width(font_id, text, strlen(text)); + const float text_width = BLF_width(font_id, text, strlen(text)); if (ypos_region - text_width / 2.0f >= ymin && ypos_region + text_width / 2.0f <= ymax) { BLF_draw_default_ascii(xpos, ypos_region - text_width / 2.0f, 0.0f, text, sizeof(text)); @@ -417,7 +417,7 @@ static void view_to_string__time( { const Scene *scene = (const Scene *)user_data; - int brevity_level = 0; + const int brevity_level = 0; BLI_timecode_string_from_time( r_str, max_len, brevity_level, v2d_pos / (float)FPS, FPS, U.timecode_style); } @@ -462,25 +462,25 @@ float UI_view2d_grid_resolution_y__values(const struct View2D *v2d) void UI_view2d_draw_lines_x__discrete_values(const View2D *v2d) { - uint major_line_distance = view2d_major_step_x__discrete(v2d); + const uint major_line_distance = view2d_major_step_x__discrete(v2d); view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v'); } void UI_view2d_draw_lines_x__values(const View2D *v2d) { - float major_line_distance = view2d_major_step_x__continuous(v2d); + const float major_line_distance = view2d_major_step_x__continuous(v2d); view2d_draw_lines(v2d, major_line_distance, true, 'v'); } void UI_view2d_draw_lines_y__values(const View2D *v2d) { - float major_line_distance = view2d_major_step_y__continuous(v2d); + const float major_line_distance = view2d_major_step_y__continuous(v2d); view2d_draw_lines(v2d, major_line_distance, true, 'h'); } void UI_view2d_draw_lines_x__discrete_time(const View2D *v2d, const Scene *scene) { - float major_line_distance = view2d_major_step_x__time(v2d, scene); + const float major_line_distance = view2d_major_step_x__time(v2d, scene); view2d_draw_lines(v2d, major_line_distance, major_line_distance > 1, 'v'); } @@ -516,7 +516,7 @@ static void UI_view2d_draw_scale_x__discrete_values(const ARegion *region, const rcti *rect, int colorid) { - float number_step = view2d_major_step_x__discrete(v2d); + const float number_step = view2d_major_step_x__discrete(v2d); draw_horizontal_scale_indicators( region, v2d, number_step, rect, view_to_string__frame_number, NULL, colorid); } @@ -524,7 +524,7 @@ static void UI_view2d_draw_scale_x__discrete_values(const ARegion *region, static void UI_view2d_draw_scale_x__discrete_time( const ARegion *region, const View2D *v2d, const rcti *rect, const Scene *scene, int colorid) { - float step = view2d_major_step_x__time(v2d, scene); + const float step = view2d_major_step_x__time(v2d, scene); draw_horizontal_scale_indicators( region, v2d, step, rect, view_to_string__time, (void *)scene, colorid); } @@ -534,7 +534,7 @@ static void UI_view2d_draw_scale_x__values(const ARegion *region, const rcti *rect, int colorid) { - float step = view2d_major_step_x__continuous(v2d); + const float step = view2d_major_step_x__continuous(v2d); draw_horizontal_scale_indicators(region, v2d, step, rect, view_to_string__value, NULL, colorid); } @@ -543,7 +543,7 @@ void UI_view2d_draw_scale_y__values(const ARegion *region, const rcti *rect, int colorid) { - float step = view2d_major_step_y__continuous(v2d); + const float step = view2d_major_step_y__continuous(v2d); draw_vertical_scale_indicators( region, v2d, step, 0.0f, rect, view_to_string__value, NULL, colorid); } diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index eb47c5c3e6d..7234e279da8 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -436,8 +436,8 @@ static float edge_pan_speed(v2dViewPanData *vpd, ARegion *region = vpd->region; /* Find the distance from the start of the drag zone. */ - int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + EDGE_PAN_REGION_PAD; - int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - EDGE_PAN_REGION_PAD; + const int min = (x_dir ? region->winrct.xmin : region->winrct.ymin) + EDGE_PAN_REGION_PAD; + const int max = (x_dir ? region->winrct.xmax : region->winrct.ymax) - EDGE_PAN_REGION_PAD; int distance = 0.0; if (event_loc > max) { distance = event_loc - max; @@ -451,8 +451,8 @@ static float edge_pan_speed(v2dViewPanData *vpd, } /* Apply a fade in to the speed based on a start time delay. */ - double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y; - float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time)); + const double start_time = x_dir ? vpd->edge_pan_start_time_x : vpd->edge_pan_start_time_y; + const float delay_factor = smootherstep(EDGE_PAN_DELAY, (float)(current_time - start_time)); return distance * EDGE_PAN_SPEED_PER_PIXEL * delay_factor; } @@ -475,7 +475,7 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event * On successful handling, always pass events on to other handlers. */ const int success_retval = OPERATOR_PASS_THROUGH; - int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X; + const int outside_padding = RNA_int_get(op->ptr, "outside_padding") * UI_UNIT_X; rcti padding_rect; if (outside_padding != 0) { padding_rect = region->winrct; @@ -504,14 +504,14 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event edge_pan_manage_delay_timers(vpd, pan_dir_x, pan_dir_y, current_time); /* Calculate the delta since the last time the operator was called. */ - float dtime = (float)(current_time - vpd->edge_pan_last_time); + const float dtime = (float)(current_time - vpd->edge_pan_last_time); float dx = 0.0f, dy = 0.0f; if (pan_dir_x != 0) { - float speed = edge_pan_speed(vpd, event->x, true, current_time); + const float speed = edge_pan_speed(vpd, event->x, true, current_time); dx = dtime * speed * (float)pan_dir_x; } if (pan_dir_y != 0) { - float speed = edge_pan_speed(vpd, event->y, false, current_time); + const float speed = edge_pan_speed(vpd, event->y, false, current_time); dy = dtime * speed * (float)pan_dir_y; } vpd->edge_pan_last_time = current_time; @@ -911,9 +911,9 @@ static void view_zoomstep_apply_ex( /* only move view to mouse if zoom fac is inside minzoom/maxzoom */ if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) || IN_RANGE_INCL(zoomx, v2d->minzoom, v2d->maxzoom)) { - float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dx) - (mval_faci * dx); + const float mval_fac = (vzd->mx_2d - cur_old.xmin) / BLI_rctf_size_x(&cur_old); + const float mval_faci = 1.0f - mval_fac; + const float ofs = (mval_fac * dx) - (mval_faci * dx); v2d->cur.xmin += ofs; v2d->cur.xmax += ofs; @@ -946,9 +946,9 @@ static void view_zoomstep_apply_ex( /* only move view to mouse if zoom fac is inside minzoom/maxzoom */ if (((v2d->keepzoom & V2D_LIMITZOOM) == 0) || IN_RANGE_INCL(zoomy, v2d->minzoom, v2d->maxzoom)) { - float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dy) - (mval_faci * dy); + const float mval_fac = (vzd->my_2d - cur_old.ymin) / BLI_rctf_size_y(&cur_old); + const float mval_faci = 1.0f - mval_fac; + const float ofs = (mval_fac * dy) - (mval_faci * dy); v2d->cur.ymin += ofs; v2d->cur.ymax += ofs; @@ -1167,8 +1167,8 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) /* continuous zoom shouldn't move that fast... */ if (U.viewzoom == USER_ZOOM_CONT) { // XXX store this setting as RNA prop? - double time = PIL_check_seconds_timer(); - float time_step = (float)(time - vzd->timer_lastdraw); + const double time = PIL_check_seconds_timer(); + const float time_step = (float)(time - vzd->timer_lastdraw); dx *= time_step * 0.5f; dy *= time_step * 0.5f; @@ -1183,9 +1183,9 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) } else { if (zoom_to_pos) { - float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dx) - (mval_faci * dx); + const float mval_fac = (vzd->mx_2d - v2d->cur.xmin) / BLI_rctf_size_x(&v2d->cur); + const float mval_faci = 1.0f - mval_fac; + const float ofs = (mval_fac * dx) - (mval_faci * dx); v2d->cur.xmin += ofs + dx; v2d->cur.xmax += ofs - dx; @@ -1202,9 +1202,9 @@ static void view_zoomdrag_apply(bContext *C, wmOperator *op) } else { if (zoom_to_pos) { - float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur); - float mval_faci = 1.0f - mval_fac; - float ofs = (mval_fac * dy) - (mval_faci * dy); + const float mval_fac = (vzd->my_2d - v2d->cur.ymin) / BLI_rctf_size_y(&v2d->cur); + const float mval_faci = 1.0f - mval_fac; + const float ofs = (mval_fac * dy) - (mval_faci * dy); v2d->cur.ymin += ofs + dy; v2d->cur.ymax += ofs - dy; @@ -1987,8 +1987,8 @@ static short mouse_in_scroller_handle(int mouse, int sc_min, int sc_max, int sh_ (mouse >= (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT))); bool in_bar = ((mouse < (sh_max - V2D_SCROLL_HANDLE_SIZE_HOTSPOT)) && (mouse > (sh_min + V2D_SCROLL_HANDLE_SIZE_HOTSPOT))); - bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT); - bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT); + const bool out_min = mouse < (sh_min - V2D_SCROLL_HANDLE_SIZE_HOTSPOT); + const bool out_max = mouse > (sh_max + V2D_SCROLL_HANDLE_SIZE_HOTSPOT); if (in_bar) { return SCROLLHANDLE_BAR; diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c index 3dc6227434e..e43eea35a91 100644 --- a/source/blender/editors/mask/mask_add.c +++ b/source/blender/editors/mask/mask_add.c @@ -462,8 +462,10 @@ static int add_vertex_handle_cyclic_at_point(bContext *C, const float tolerance_in_pixels_squared = 4 * 4; if (spline->flag & MASK_SPLINE_CYCLIC) { - /* No cycling toggle needed, we've got nothing meaningful to do in this operator. */ - return OPERATOR_CANCELLED; + /* The spline is already cyclic, so there is no need to handle anything here. + * Return PASS_THROUGH so that it's possible to add vertices close to the endpoints of the + * cyclic spline. */ + return OPERATOR_PASS_THROUGH; } float co_pixel[2]; diff --git a/source/blender/editors/mask/mask_draw.c b/source/blender/editors/mask/mask_draw.c index dbaa335a9bf..8acbb328ab0 100644 --- a/source/blender/editors/mask/mask_draw.c +++ b/source/blender/editors/mask/mask_draw.c @@ -750,7 +750,7 @@ void ED_mask_draw_region( IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GL_R16F, false, buffer, 1.0f, 1.0f, NULL); + immDrawPixelsTex(&state, 0.0f, 0.0f, width, height, GPU_R16F, false, buffer, 1.0f, 1.0f, NULL); GPU_matrix_pop(); diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index e41445aef09..589b51ce942 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -93,6 +93,10 @@ if(WITH_BULLET) add_definitions(-DWITH_BULLET) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + add_definitions(${GL_DEFINITIONS}) blender_add_lib(bf_editor_mesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index 97bd6ee0039..d56daaf8094 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -39,6 +39,9 @@ #include "WM_types.h" +#include "UI_interface.h" +#include "UI_resources.h" + #include "ED_mesh.h" #include "ED_screen.h" @@ -46,6 +49,7 @@ #include "mesh_intern.h" /* own include */ +#include "tools/bmesh_boolean.h" #include "tools/bmesh_intersect.h" #include "tools/bmesh_separate.h" @@ -134,6 +138,11 @@ enum { ISECT_SEPARATE_NONE = 2, }; +enum { + ISECT_SOLVER_FAST = 0, + ISECT_SOLVER_EXACT = 1, +}; + static int edbm_intersect_exec(bContext *C, wmOperator *op) { const int mode = RNA_enum_get(op->ptr, "mode"); @@ -142,6 +151,11 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) bool use_separate_cut = false; const int separate_mode = RNA_enum_get(op->ptr, "separate_mode"); const float eps = RNA_float_get(op->ptr, "threshold"); +#ifdef WITH_GMP + const bool exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT; +#else + const bool exact = false; +#endif bool use_self; bool has_isect; @@ -186,19 +200,25 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) continue; } - has_isect = BM_mesh_intersect(em->bm, - em->looptris, - em->tottri, - test_fn, - NULL, - use_self, - use_separate_all, - true, - true, - true, - true, - -1, - eps); + if (exact) { + has_isect = BM_mesh_boolean_knife( + em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, use_separate_all); + } + else { + has_isect = BM_mesh_intersect(em->bm, + em->looptris, + em->tottri, + test_fn, + NULL, + use_self, + use_separate_all, + true, + true, + true, + true, + -1, + eps); + } if (use_separate_cut) { /* detach selected/un-selected faces */ @@ -220,6 +240,38 @@ static int edbm_intersect_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void edbm_intersect_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + +#ifdef WITH_GMP + bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT; +#else + bool use_exact = false; +#endif + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "separate_mode", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); +#ifdef WITH_GMP + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); +#endif + if (!use_exact) { + uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE); + } +} + void MESH_OT_intersect(struct wmOperatorType *ot) { static const EnumPropertyItem isect_mode_items[] = { @@ -243,6 +295,12 @@ void MESH_OT_intersect(struct wmOperatorType *ot) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem isect_intersect_solver_items[] = { + {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster Solver, some limitations"}, + {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact Solver, slower, handles more cases"}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ ot->name = "Intersect (Knife)"; ot->description = "Cut an intersection into faces"; @@ -251,6 +309,7 @@ void MESH_OT_intersect(struct wmOperatorType *ot) /* api callbacks */ ot->exec = edbm_intersect_exec; ot->poll = ED_operator_editmesh; + ot->ui = edbm_intersect_ui; /* props */ RNA_def_enum(ot->srna, "mode", isect_mode_items, ISECT_SEL_UNSEL, "Source", ""); @@ -258,6 +317,14 @@ void MESH_OT_intersect(struct wmOperatorType *ot) ot->srna, "separate_mode", isect_separate_items, ISECT_SEPARATE_CUT, "Separate Mode", ""); RNA_def_float_distance( ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001); +#ifdef WITH_GMP + RNA_def_enum(ot->srna, + "solver", + isect_intersect_solver_items, + ISECT_SOLVER_EXACT, + "Solver", + "Which Intersect solver to use"); +#endif /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -280,6 +347,12 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) { const int boolean_operation = RNA_enum_get(op->ptr, "operation"); bool use_swap = RNA_boolean_get(op->ptr, "use_swap"); + bool use_self = RNA_boolean_get(op->ptr, "use_self"); +#ifdef WITH_GMP + bool use_exact = RNA_enum_get(op->ptr, "solver") == ISECT_SOLVER_EXACT; +#else + bool use_exact = false; +#endif const float eps = RNA_float_get(op->ptr, "threshold"); int (*test_fn)(BMFace *, void *); bool has_isect; @@ -298,19 +371,25 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) continue; } - has_isect = BM_mesh_intersect(em->bm, - em->looptris, - em->tottri, - test_fn, - NULL, - false, - false, - true, - true, - false, - true, - boolean_operation, - eps); + if (use_exact) { + has_isect = BM_mesh_boolean( + em->bm, em->looptris, em->tottri, test_fn, NULL, use_self, boolean_operation); + } + else { + has_isect = BM_mesh_intersect(em->bm, + em->looptris, + em->tottri, + test_fn, + NULL, + false, + false, + true, + true, + false, + true, + boolean_operation, + eps); + } edbm_intersect_select(em, obedit->data, has_isect); @@ -326,6 +405,38 @@ static int edbm_intersect_boolean_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static void edbm_intersect_boolean_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayout *row; + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + +#ifdef WITH_GMP + bool use_exact = RNA_enum_get(&ptr, "solver") == ISECT_SOLVER_EXACT; +#else + bool use_exact = false; +#endif + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "operation", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); +#ifdef WITH_GMP + row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemS(layout); +#endif + uiItemR(layout, &ptr, "use_swap", 0, NULL, ICON_NONE); + uiItemR(layout, &ptr, "use_self", 0, NULL, ICON_NONE); + if (!use_exact) { + uiItemR(layout, &ptr, "threshold", 0, NULL, ICON_NONE); + } +} + void MESH_OT_intersect_boolean(struct wmOperatorType *ot) { static const EnumPropertyItem isect_boolean_operation_items[] = { @@ -334,6 +445,11 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot) {BMESH_ISECT_BOOLEAN_DIFFERENCE, "DIFFERENCE", 0, "Difference", ""}, {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem isect_boolean_solver_items[] = { + {ISECT_SOLVER_FAST, "FAST", 0, "Fast", "Faster Solver, some limitations"}, + {ISECT_SOLVER_EXACT, "EXACT", 0, "Exact", "Exact Solver, slower, handles more cases"}, + {0, NULL, 0, NULL, NULL}, + }; /* identifiers */ ot->name = "Intersect (Boolean)"; @@ -343,21 +459,31 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot) /* api callbacks */ ot->exec = edbm_intersect_boolean_exec; ot->poll = ED_operator_editmesh; + ot->ui = edbm_intersect_boolean_ui; /* props */ RNA_def_enum(ot->srna, "operation", isect_boolean_operation_items, BMESH_ISECT_BOOLEAN_DIFFERENCE, - "Boolean", - ""); + "Boolean operation", + "Which boolean operation to apply"); RNA_def_boolean(ot->srna, "use_swap", false, "Swap", "Use with difference intersection to swap which side is kept"); + RNA_def_boolean(ot->srna, "use_self", false, "Self", "Do self-union or self-intersection"); RNA_def_float_distance( ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge threshold", "", 0.0, 0.001); +#ifdef WITH_GMP + RNA_def_enum(ot->srna, + "solver", + isect_boolean_solver_items, + ISECT_SOLVER_EXACT, + "Solver", + "Which Boolean solver to use"); +#endif /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 94cd7650abe..6facee77c1e 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1051,7 +1051,7 @@ static void knife_init_colors(KnifeColors *colors) static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg) { const KnifeTool_OpData *kcd = arg; - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_matrix_push_projection(); GPU_polygon_offset(1.0f, 1.0f); @@ -1222,7 +1222,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_matrix_pop_projection(); /* Reset default */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } /** diff --git a/source/blender/editors/mesh/editmesh_preselect_edgering.c b/source/blender/editors/mesh/editmesh_preselect_edgering.c index d9bd63ef35f..aa1df3d76fc 100644 --- a/source/blender/editors/mesh/editmesh_preselect_edgering.c +++ b/source/blender/editors/mesh/editmesh_preselect_edgering.c @@ -159,7 +159,7 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl return; } - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_matrix_push(); GPU_matrix_mul(matrix); @@ -197,7 +197,7 @@ void EDBM_preselect_edgering_draw(struct EditMesh_PreSelEdgeRing *psel, const fl GPU_matrix_pop(); /* Reset default */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } static void view3d_preselect_mesh_edgering_update_verts_from_edge( diff --git a/source/blender/editors/mesh/editmesh_preselect_elem.c b/source/blender/editors/mesh/editmesh_preselect_elem.c index d53a1e2b55c..dfd646c767f 100644 --- a/source/blender/editors/mesh/editmesh_preselect_elem.c +++ b/source/blender/editors/mesh/editmesh_preselect_elem.c @@ -133,7 +133,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr return; } - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_matrix_push(); GPU_matrix_mul(matrix); @@ -204,7 +204,7 @@ void EDBM_preselect_elem_draw(struct EditMesh_PreSelElem *psel, const float matr GPU_matrix_pop(); /* Reset default */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } static void view3d_preselect_mesh_elem_update_from_vert(struct EditMesh_PreSelElem *psel, diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index d2e9b57e950..efcf4abda06 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1423,15 +1423,13 @@ void MESH_OT_select_mode(wmOperatorType *ot) static void walker_select_count(BMEditMesh *em, int walkercode, void *start, - const bool select, - const bool select_mix, - int *r_totsel, - int *r_totunsel) + int r_count_by_select[2]) { BMesh *bm = em->bm; BMElem *ele; BMWalker walker; - int tot[2] = {0, 0}; + + r_count_by_select[0] = r_count_by_select[1] = 0; BMW_init(&walker, bm, @@ -1443,17 +1441,15 @@ static void walker_select_count(BMEditMesh *em, BMW_NIL_LAY); for (ele = BMW_begin(&walker, start); ele; ele = BMW_step(&walker)) { - tot[(BM_elem_flag_test_bool(ele, BM_ELEM_SELECT) != select)] += 1; + r_count_by_select[BM_elem_flag_test(ele, BM_ELEM_SELECT) ? 1 : 0] += 1; - if (!select_mix && tot[0] && tot[1]) { - tot[0] = tot[1] = -1; + /* Early exit when mixed (could be optional if needed. */ + if (r_count_by_select[0] && r_count_by_select[1]) { + r_count_by_select[0] = r_count_by_select[1] = -1; break; } } - *r_totsel = tot[0]; - *r_totunsel = tot[1]; - BMW_end(&walker); } @@ -1590,18 +1586,18 @@ static void mouse_mesh_loop_edge( { bool edge_boundary = false; - /* cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY */ + /* Cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY. */ if (select_cycle && BM_edge_is_boundary(eed)) { - int tot[2]; + int count_by_select[2]; - /* if the loops selected toggle the boundaries */ - walker_select_count(em, BMW_EDGELOOP, eed, select, false, &tot[0], &tot[1]); - if (tot[select] == 0) { + /* If the loops selected toggle the boundaries. */ + walker_select_count(em, BMW_EDGELOOP, eed, count_by_select); + if (count_by_select[!select] == 0) { edge_boundary = true; - /* if the boundaries selected, toggle back to the loop */ - walker_select_count(em, BMW_EDGEBOUNDARY, eed, select, false, &tot[0], &tot[1]); - if (tot[select] == 0) { + /* If the boundaries selected, toggle back to the loop. */ + walker_select_count(em, BMW_EDGEBOUNDARY, eed, count_by_select); + if (count_by_select[!select] == 0) { edge_boundary = false; } } diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index 46c63d2e057..e454328e2d4 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -523,7 +523,7 @@ void EDBM_flag_enable_all(BMEditMesh *em, const char hflag) * \{ */ /** - * Return a new UVVertMap from the editmesh + * Return a new #UvVertMap from the edit-mesh. */ UvVertMap *BM_uv_vert_map_create(BMesh *bm, const bool use_select, const bool use_winding) { diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c index f608e5ce6a5..22ea222cf01 100644 --- a/source/blender/editors/mesh/mesh_data.c +++ b/source/blender/editors/mesh/mesh_data.c @@ -1014,6 +1014,10 @@ static int mesh_customdata_custom_splitnormals_clear_exec(bContext *C, wmOperato Mesh *me = ED_mesh_context(C); if (BKE_mesh_has_custom_loop_normals(me)) { + BMEditMesh *em = me->edit_mesh; + if (em != NULL && em->bm->lnor_spacearr != NULL) { + BKE_lnor_spacearr_clear(em->bm->lnor_spacearr); + } return mesh_customdata_clear_exec__internal(C, BM_LOOP, CD_CUSTOMLOOPNORMAL); } return OPERATOR_CANCELLED; diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 5278da67777..bd14919d1d7 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -590,8 +590,9 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) loopofs = 0; polyofs = 0; - /* inverse transform for all selected meshes in this object */ - invert_m4_m4(imat, ob->obmat); + /* Inverse transform for all selected meshes in this object, + * See #object_join_exec for detailed comment on why the safe version is used. */ + invert_m4_m4_safe_ortho(imat, ob->obmat); /* Add back active mesh first. * This allows to keep things similar as they were, as much as possible @@ -741,6 +742,7 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 139900d0a4d..72180d58ecb 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -1450,6 +1450,7 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op) DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } @@ -2783,6 +2784,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, scene); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } @@ -3003,6 +3005,7 @@ static int duplicate_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); return OPERATOR_FINISHED; } @@ -3094,6 +3097,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; @@ -3163,20 +3167,45 @@ static int object_join_exec(bContext *C, wmOperator *op) } } + int ret = OPERATOR_CANCELLED; if (ob->type == OB_MESH) { - return ED_mesh_join_objects_exec(C, op); - } - if (ELEM(ob->type, OB_CURVE, OB_SURF)) { - return ED_curve_join_objects_exec(C, op); - } - if (ob->type == OB_ARMATURE) { - return ED_armature_join_objects_exec(C, op); - } - if (ob->type == OB_GPENCIL) { - return ED_gpencil_join_objects_exec(C, op); - } - - return OPERATOR_CANCELLED; + ret = ED_mesh_join_objects_exec(C, op); + } + else if (ELEM(ob->type, OB_CURVE, OB_SURF)) { + ret = ED_curve_join_objects_exec(C, op); + } + else if (ob->type == OB_ARMATURE) { + ret = ED_armature_join_objects_exec(C, op); + } + else if (ob->type == OB_GPENCIL) { + ret = ED_gpencil_join_objects_exec(C, op); + } + + if (ret & OPERATOR_FINISHED) { + /* Even though internally failure to invert is accounted for with a fallback, + * show a warning since the result may not be what the user expects. See T80077. + * + * Failure to invert the matrix is typically caused by zero scaled axes + * (which can be caused by constraints, even if the input scale isn't zero). + * + * Internally the join functions use #invert_m4_m4_safe_ortho which creates + * an inevitable matrix from one that has one or more degenerate axes. + * + * In most cases we don't worry about special handling for non-inevitable matrices however for + * joining objects there may be flat 2D objects where it's not obvious the scale is zero. + * In this case, using #invert_m4_m4_safe_ortho works as well as we can expect, + * joining the contents, flattening on the axis that's zero scaled. + * If the zero scale is removed, the data on this axis remains un-scaled + * (something that wouldn't work for #invert_m4_m4_safe). */ + float imat_test[4][4]; + if (!invert_m4_m4(imat_test, ob->obmat)) { + BKE_report(op->reports, + RPT_WARNING, + "Active object final transform has one or more zero scaled axes"); + } + } + + return ret; } void OBJECT_OT_join(wmOperatorType *ot) diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 6bc615c9b9e..5c7370334e8 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -303,6 +303,7 @@ void OBJECT_OT_voxel_remesh(struct wmOperatorType *ot); void OBJECT_OT_voxel_size_edit(struct wmOperatorType *ot); void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot); void OBJECT_OT_tetgen_remesh(struct wmOperatorType *ot); +void OBJECT_OT_tetlattice_remesh(struct wmOperatorType *ot); /* object_transfer_data.c */ void OBJECT_OT_data_transfer(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index c699882ef4a..763d826dbde 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -274,6 +274,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_quadriflow_remesh); WM_operatortype_append(OBJECT_OT_tetgen_remesh); + WM_operatortype_append(OBJECT_OT_tetlattice_remesh); } void ED_operatormacros_object(void) diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 732d2f6ad52..d9196425098 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -134,13 +134,11 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = CTX_data_edit_object(C); - BMVert *eve; - BMIter iter; - Nurb *nu; - BezTriple *bezt; - BPoint *bp; Object *par; - int a, v1 = 0, v2 = 0, v3 = 0, v4 = 0, nr = 1; + +#define INDEX_UNSET -1 + int par1, par2, par3, par4; + par1 = par2 = par3 = par4 = INDEX_UNSET; /* we need 1 to 3 selected vertices */ @@ -165,114 +163,108 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) * objects are also up to date. */ BKE_scene_graph_update_tagged(depsgraph, bmain); - BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { + BMVert *eve; + BMIter iter; + int curr_index; + BM_ITER_MESH_INDEX (eve, &iter, em->bm, BM_VERTS_OF_MESH, curr_index) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { - if (v1 == 0) { - v1 = nr; + if (par1 == INDEX_UNSET) { + par1 = curr_index; } - else if (v2 == 0) { - v2 = nr; + else if (par2 == INDEX_UNSET) { + par2 = curr_index; } - else if (v3 == 0) { - v3 = nr; + else if (par3 == INDEX_UNSET) { + par3 = curr_index; } - else if (v4 == 0) { - v4 = nr; + else if (par4 == INDEX_UNSET) { + par4 = curr_index; } else { break; } } - nr++; } } else if (ELEM(obedit->type, OB_SURF, OB_CURVE)) { ListBase *editnurb = object_editcurve_get(obedit); - nu = editnurb->first; - while (nu) { + for (Nurb *nu = editnurb->first; nu != NULL; nu = nu->next) { if (nu->type == CU_BEZIER) { - bezt = nu->bezt; - a = nu->pntsu; - while (a--) { + BezTriple *bezt = nu->bezt; + for (int curr_index = 0; curr_index < nu->pntsu; curr_index++, bezt++) { if (BEZT_ISSEL_ANY_HIDDENHANDLES(v3d, bezt)) { - if (v1 == 0) { - v1 = nr; + if (par1 == INDEX_UNSET) { + par1 = curr_index; } - else if (v2 == 0) { - v2 = nr; + else if (par2 == INDEX_UNSET) { + par2 = curr_index; } - else if (v3 == 0) { - v3 = nr; + else if (par3 == INDEX_UNSET) { + par3 = curr_index; } - else if (v4 == 0) { - v4 = nr; + else if (par4 == INDEX_UNSET) { + par4 = curr_index; } else { break; } } - nr++; - bezt++; } } else { - bp = nu->bp; - a = nu->pntsu * nu->pntsv; - while (a--) { + BPoint *bp = nu->bp; + const int num_points = nu->pntsu * nu->pntsv; + for (int curr_index = 0; curr_index < num_points; curr_index++, bp++) { if (bp->f1 & SELECT) { - if (v1 == 0) { - v1 = nr; + if (par1 == INDEX_UNSET) { + par1 = curr_index; } - else if (v2 == 0) { - v2 = nr; + else if (par2 == INDEX_UNSET) { + par2 = curr_index; } - else if (v3 == 0) { - v3 = nr; + else if (par3 == INDEX_UNSET) { + par3 = curr_index; } - else if (v4 == 0) { - v4 = nr; + else if (par4 == INDEX_UNSET) { + par4 = curr_index; } else { break; } } - nr++; - bp++; } } - nu = nu->next; } } else if (obedit->type == OB_LATTICE) { Lattice *lt = obedit->data; - a = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw; - bp = lt->editlatt->latt->def; - while (a--) { + const int num_points = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * + lt->editlatt->latt->pntsw; + BPoint *bp = lt->editlatt->latt->def; + for (int curr_index = 0; curr_index < num_points; curr_index++, bp++) { if (bp->f1 & SELECT) { - if (v1 == 0) { - v1 = nr; + if (par1 == INDEX_UNSET) { + par1 = curr_index; } - else if (v2 == 0) { - v2 = nr; + else if (par2 == INDEX_UNSET) { + par2 = curr_index; } - else if (v3 == 0) { - v3 = nr; + else if (par3 == INDEX_UNSET) { + par3 = curr_index; } - else if (v4 == 0) { - v4 = nr; + else if (par4 == INDEX_UNSET) { + par4 = curr_index; } else { break; } } - nr++; - bp++; } } - if (v4 || !((v1 && v2 == 0 && v3 == 0) || (v1 && v2 && v3))) { + if (par4 != INDEX_UNSET || par1 == INDEX_UNSET || (par2 != INDEX_UNSET && par3 == INDEX_UNSET)) { BKE_report(op->reports, RPT_ERROR, "Select either 1 or 3 vertices to parent to"); return OPERATOR_CANCELLED; } @@ -289,11 +281,11 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) Object workob; ob->parent = BASACT(view_layer)->object; - if (v3) { + if (par3 != INDEX_UNSET) { ob->partype = PARVERT3; - ob->par1 = v1 - 1; - ob->par2 = v2 - 1; - ob->par3 = v3 - 1; + ob->par1 = par1; + ob->par2 = par2; + ob->par3 = par3; /* inverse parent matrix */ BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob); @@ -301,7 +293,7 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) } else { ob->partype = PARVERT1; - ob->par1 = v1 - 1; + ob->par1 = par1; /* inverse parent matrix */ BKE_object_workob_calc_parent(depsgraph, scene, ob, &workob); @@ -317,6 +309,8 @@ static int vertex_parent_set_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_OBJECT, NULL); return OPERATOR_FINISHED; + +#undef INDEX_UNSET } void OBJECT_OT_vertex_parent_set(wmOperatorType *ot) @@ -1474,7 +1468,8 @@ static int make_links_scene_exec(bContext *C, wmOperator *op) /* redraw the 3D view because the object center points are colored differently */ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); - /* one day multiple scenes will be visible, then we should have some update function for them */ + /* one day multiple scenes will be visible, then we should have some update function for them + */ return OPERATOR_FINISHED; } @@ -1794,9 +1789,9 @@ static Collection *single_object_users_collection(Main *bmain, if (is_master_collection && copy_collections && child->collection != collection_child_new) { /* We do not want a collection sync here, our collections are in a complete uninitialized - * state currently. With current code, that would lead to a memory leak - because of reasons. - * It would be a useless loss of computing anyway, since caller has to fully refresh - * view-layers/collections caching at the end. */ + * state currently. With current code, that would lead to a memory leak - because of + * reasons. It would be a useless loss of computing anyway, since caller has to fully + * refresh view-layers/collections caching at the end. */ BKE_collection_child_add_no_sync(collection, collection_child_new); BLI_remlink(&collection->children, child); MEM_freeN(child); diff --git a/source/blender/editors/object/object_remesh.c b/source/blender/editors/object/object_remesh.c index 9be84dc4b8c..a47f6329bae 100644 --- a/source/blender/editors/object/object_remesh.c +++ b/source/blender/editors/object/object_remesh.c @@ -1252,4 +1252,57 @@ void OBJECT_OT_tetgen_remesh(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static int tetlattice_remesh_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + + Mesh *mesh = ob->data; + Mesh *new_mesh = NULL; + + unsigned int *tets = NULL; + int numtets; + int subdiv = 3; + new_mesh = BKE_mesh_remesh_tetlattice_to_mesh_nomain(mesh, subdiv, &tets, &numtets); + if (tets) { + MEM_freeN(tets); + } + + if (!new_mesh) { + BKE_report(op->reports, RPT_ERROR, "Tet Lattice remesher failed to create mesh"); + return OPERATOR_CANCELLED; + } + + BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob, &CD_MASK_MESH, true); + + if (mesh->flag & ME_REMESH_SMOOTH_NORMALS) { + BKE_mesh_smooth_flag_set(ob->data, true); + } + + if (ob->mode == OB_MODE_SCULPT) { + ED_sculpt_undo_geometry_end(ob); + } + + BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_tetlattice_remesh(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Tet Lattice Remesh"; + ot->description = + "Create a new tet mesh using the surface data of the current mesh. All data " + "layers will be lost"; + ot->idname = "OBJECT_OT_tetlattice_remesh"; + + /* api callbacks */ + ot->poll = object_remesh_poll; + ot->exec = tetlattice_remesh_exec; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /** \} */ diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 52a7b92217b..940ca963fc6 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -339,7 +339,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R GPU_offscreen_bind(oglrender->ofs, true); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT); + GPU_clear_depth(1.0f); GPU_matrix_reset(); wmOrtho2(0, scene->r.xsch, 0, scene->r.ysch); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 711f89b9fda..3f31cbf7e48 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -95,6 +95,10 @@ #include "render_intern.h" // own include +/* -------------------------------------------------------------------- */ +/** \name Local Utilities + * \{ */ + /** * Object list for material operations. * has exception for pinned object. @@ -127,7 +131,11 @@ static Object **object_array_for_shading(bContext *C, uint *r_objects_len) return objects; } -/********************** material slot operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Add Operator + * \{ */ static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -168,6 +176,12 @@ void OBJECT_OT_material_slot_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Remove Operator + * \{ */ + static int material_slot_remove_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); @@ -213,6 +227,12 @@ void OBJECT_OT_material_slot_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Assign Operator + * \{ */ + static int material_slot_assign_exec(bContext *C, wmOperator *UNUSED(op)) { View3D *v3d = CTX_wm_view3d(C); @@ -316,6 +336,12 @@ void OBJECT_OT_material_slot_assign(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot (De)Select Operator + * \{ */ + static int material_slot_de_select(bContext *C, bool select) { bool changed_multi = false; @@ -461,6 +487,12 @@ void OBJECT_OT_material_slot_deselect(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Copy Operator + * \{ */ + static int material_slot_copy_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -515,6 +547,12 @@ void OBJECT_OT_material_slot_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Move Operator + * \{ */ + static int material_slot_move_exec(bContext *C, wmOperator *op) { Object *ob = ED_object_context(C); @@ -590,6 +628,12 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot) "Direction to move the active material towards"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Slot Remove Unused Operator + * \{ */ + static int material_slot_remove_unused_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); @@ -657,7 +701,11 @@ void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/********************** new material operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name New Material Operator + * \{ */ static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -727,7 +775,11 @@ void MATERIAL_OT_new(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/********************** new texture operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name New Texture Operator + * \{ */ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -776,7 +828,11 @@ void TEXTURE_OT_new(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/********************** new world operator *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name new world operator + * \{ */ static int new_world_exec(bContext *C, wmOperator *UNUSED(op)) { @@ -827,7 +883,11 @@ void WORLD_OT_new(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/********************** render layer operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render Layer Add Operator + * \{ */ static int view_layer_add_exec(bContext *C, wmOperator *op) { @@ -877,6 +937,12 @@ void SCENE_OT_view_layer_add(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render Layer Remove Operator + * \{ */ + static bool view_layer_remove_poll(bContext *C) { Scene *scene = CTX_data_scene(C); @@ -913,7 +979,12 @@ void SCENE_OT_view_layer_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } -/********************** light cache operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Cache Bake Operator + * \{ */ + enum { LIGHTCACHE_SUBSET_ALL = 0, LIGHTCACHE_SUBSET_DIRTY, @@ -1079,6 +1150,12 @@ void SCENE_OT_light_cache_bake(wmOperatorType *ot) RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Light Cache Free Operator + * \{ */ + static bool light_cache_free_poll(bContext *C) { Scene *scene = CTX_data_scene(C); @@ -1122,7 +1199,11 @@ void SCENE_OT_light_cache_free(wmOperatorType *ot) ot->poll = light_cache_free_poll; } -/********************** render view operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render View Remove Operator + * \{ */ static bool render_view_remove_poll(bContext *C) { @@ -1158,6 +1239,12 @@ void SCENE_OT_render_view_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Render View Add Operator + * \{ */ + static int render_view_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1187,8 +1274,14 @@ void SCENE_OT_render_view_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/** \} */ + #ifdef WITH_FREESTYLE +/* -------------------------------------------------------------------- */ +/** \name Free Style Module Add Operator + * \{ */ + static bool freestyle_linestyle_check_report(FreestyleLineSet *lineset, ReportList *reports) { if (!lineset) { @@ -1241,6 +1334,12 @@ void SCENE_OT_freestyle_module_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Module Remove Operator + * \{ */ + static int freestyle_module_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1287,6 +1386,12 @@ static int freestyle_module_move_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Module Move Operator + * \{ */ + void SCENE_OT_freestyle_module_move(wmOperatorType *ot) { static const EnumPropertyItem direction_items[] = { @@ -1316,6 +1421,12 @@ void SCENE_OT_freestyle_module_move(wmOperatorType *ot) "Direction to move the chosen style module towards"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Add Operator + * \{ */ + static int freestyle_lineset_add_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); @@ -1344,6 +1455,12 @@ void SCENE_OT_freestyle_lineset_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Copy Operator + * \{ */ + static bool freestyle_active_lineset_poll(bContext *C) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1379,6 +1496,12 @@ void SCENE_OT_freestyle_lineset_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Paste Operator + * \{ */ + static int freestyle_lineset_paste_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1407,6 +1530,12 @@ void SCENE_OT_freestyle_lineset_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Remove Operator + * \{ */ + static int freestyle_lineset_remove_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -1435,6 +1564,12 @@ void SCENE_OT_freestyle_lineset_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set Move Operator + * \{ */ + static int freestyle_lineset_move_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -1478,6 +1613,12 @@ void SCENE_OT_freestyle_lineset_move(wmOperatorType *ot) "Direction to move the active line set towards"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Line Set New Operator + * \{ */ + static int freestyle_linestyle_new_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1516,6 +1657,12 @@ void SCENE_OT_freestyle_linestyle_new(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Add "Color" Operator + * \{ */ + static int freestyle_color_modifier_add_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1557,6 +1704,12 @@ void SCENE_OT_freestyle_color_modifier_add(wmOperatorType *ot) ot->srna, "type", rna_enum_linestyle_color_modifier_type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Add "Alpha" Operator + * \{ */ + static int freestyle_alpha_modifier_add_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1598,6 +1751,12 @@ void SCENE_OT_freestyle_alpha_modifier_add(wmOperatorType *ot) ot->srna, "type", rna_enum_linestyle_alpha_modifier_type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Add "Thickness" Operator + * \{ */ + static int freestyle_thickness_modifier_add_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1639,6 +1798,12 @@ void SCENE_OT_freestyle_thickness_modifier_add(wmOperatorType *ot) ot->srna, "type", rna_enum_linestyle_thickness_modifier_type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Add "Geometry" Operator + * \{ */ + static int freestyle_geometry_modifier_add_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1680,6 +1845,12 @@ void SCENE_OT_freestyle_geometry_modifier_add(wmOperatorType *ot) ot->srna, "type", rna_enum_linestyle_geometry_modifier_type_items, 0, "Type", ""); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Remove Operator + * \{ */ + static int freestyle_get_modifier_type(PointerRNA *ptr) { if (RNA_struct_is_a(ptr->type, &RNA_LineStyleColorModifier)) { @@ -1747,6 +1918,12 @@ void SCENE_OT_freestyle_modifier_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Copy Operator + * \{ */ + static int freestyle_modifier_copy_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1797,6 +1974,12 @@ void SCENE_OT_freestyle_modifier_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Modifier Move Operator + * \{ */ + static int freestyle_modifier_move_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); @@ -1866,6 +2049,12 @@ void SCENE_OT_freestyle_modifier_move(wmOperatorType *ot) "Direction to move the chosen modifier towards"); } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Free Style Stroke Material Create Operator + * \{ */ + static int freestyle_stroke_material_create_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -1898,6 +2087,12 @@ void SCENE_OT_freestyle_stroke_material_create(wmOperatorType *ot) #endif /* WITH_FREESTYLE */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture Slot Move Operator + * \{ */ + static int texture_slot_move_exec(bContext *C, wmOperator *op) { ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; @@ -1966,7 +2161,11 @@ void TEXTURE_OT_slot_move(wmOperatorType *ot) RNA_def_enum(ot->srna, "type", slot_move, 0, "Type", ""); } -/********************** material operators *********************/ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Copy Operator + * \{ */ /* material copy/paste */ static int copy_material_exec(bContext *C, wmOperator *UNUSED(op)) @@ -1997,6 +2196,12 @@ void MATERIAL_OT_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Material Paste Operator + * \{ */ + static int paste_material_exec(bContext *C, wmOperator *UNUSED(op)) { Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data; @@ -2027,6 +2232,12 @@ void MATERIAL_OT_paste(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MTex Copy/Paste Utilities + * \{ */ + static short mtexcopied = 0; /* must be reset on file load */ static MTex mtexcopybuf; @@ -2093,6 +2304,12 @@ static void paste_mtex_copybuf(ID *id) } } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture Slot Copy Operator + * \{ */ + static int copy_mtex_exec(bContext *C, wmOperator *UNUSED(op)) { ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; @@ -2131,6 +2348,12 @@ void TEXTURE_OT_slot_copy(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Texture Slot Paste Operator + * \{ */ + static int paste_mtex_exec(bContext *C, wmOperator *UNUSED(op)) { ID *id = CTX_data_pointer_get_type(C, "texture_slot", &RNA_TextureSlot).owner_id; @@ -2185,3 +2408,5 @@ void TEXTURE_OT_slot_paste(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } + +/** \} */ diff --git a/source/blender/editors/render/render_update.c b/source/blender/editors/render/render_update.c index 7d0ad42c703..ce454d5eac2 100644 --- a/source/blender/editors/render/render_update.c +++ b/source/blender/editors/render/render_update.c @@ -194,7 +194,7 @@ void ED_render_engine_changed(Main *bmain, const bool update_scene_data) update_ctx.scene = scene; LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { /* TDODO(sergey): Iterate over depsgraphs instead? */ - update_ctx.depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + update_ctx.depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); update_ctx.view_layer = view_layer; ED_render_id_flush_update(&update_ctx, &scene->id); } diff --git a/source/blender/editors/scene/scene_edit.c b/source/blender/editors/scene/scene_edit.c index d599c1cbcf0..47edb322701 100644 --- a/source/blender/editors/scene/scene_edit.c +++ b/source/blender/editors/scene/scene_edit.c @@ -116,7 +116,7 @@ bool ED_scene_delete(bContext *C, Main *bmain, Scene *scene) /* Depsgraph updates after scene becomes active in a window. */ void ED_scene_change_update(Main *bmain, Scene *scene, ViewLayer *layer) { - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, layer); BKE_scene_set_background(bmain, scene); DEG_graph_relations_update(depsgraph); diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 5004b0132c2..921cc92299e 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -302,8 +302,6 @@ static void area_azone_tag_update(ScrArea *area) static void region_draw_azones(ScrArea *area, ARegion *region) { - AZone *az; - if (!area) { return; } @@ -314,7 +312,7 @@ static void region_draw_azones(ScrArea *area, ARegion *region) GPU_matrix_push(); GPU_matrix_translate_2f(-region->winrct.xmin, -region->winrct.ymin); - for (az = area->actionzones.first; az; az = az->next) { + LISTBASE_FOREACH (AZone *, az, &area->actionzones) { /* test if action zone is over this region */ rcti azrct; BLI_rcti_init(&azrct, az->x1, az->x2, az->y1, az->y2); @@ -353,11 +351,9 @@ static void region_draw_status_text(ScrArea *area, ARegion *region) if (overlap) { GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - GPU_clear(GPU_COLOR_BIT); } else { UI_ThemeClearColor(TH_HEADER); - GPU_clear(GPU_COLOR_BIT); } int fontid = BLF_set_default(); @@ -521,7 +517,6 @@ void ED_region_do_draw(bContext *C, ARegion *region) if (area && area_is_pseudo_minimized(area)) { UI_ThemeClearColor(TH_EDITOR_OUTLINE); - GPU_clear(GPU_COLOR_BIT); return; } /* optional header info instead? */ @@ -705,10 +700,8 @@ void ED_region_tag_redraw_partial(ARegion *region, const rcti *rct, bool rebuild void ED_area_tag_redraw(ScrArea *area) { - ARegion *region; - if (area) { - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { ED_region_tag_redraw(region); } } @@ -716,10 +709,8 @@ void ED_area_tag_redraw(ScrArea *area) void ED_area_tag_redraw_no_rebuild(ScrArea *area) { - ARegion *region; - if (area) { - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { ED_region_tag_redraw_no_rebuild(region); } } @@ -727,10 +718,8 @@ void ED_area_tag_redraw_no_rebuild(ScrArea *area) void ED_area_tag_redraw_regiontype(ScrArea *area, int regiontype) { - ARegion *region; - if (area) { - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == regiontype) { ED_region_tag_redraw(region); } @@ -750,14 +739,12 @@ void ED_area_tag_refresh(ScrArea *area) /* use NULL to disable it */ void ED_area_status_text(ScrArea *area, const char *str) { - ARegion *region; - /* happens when running transform operators in background mode */ if (area == NULL) { return; } - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_HEADER) { if (str) { if (region->headerstr == NULL) { @@ -942,7 +929,6 @@ static void region_azone_edge(AZone *az, ARegion *region) /* region already made zero sized, in shape of edge */ static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region) { - AZone *azt; int tot = 0, add; /* Edge offset multiplied by the */ @@ -950,7 +936,7 @@ static void region_azone_tab_plus(ScrArea *area, AZone *az, ARegion *region) const float tab_size_x = 0.7f * U.widget_unit; const float tab_size_y = 0.4f * U.widget_unit; - for (azt = area->actionzones.first; azt; azt = azt->next) { + LISTBASE_FOREACH (AZone *, azt, &area->actionzones) { if (azt->edge == az->edge) { tot++; } @@ -1846,7 +1832,6 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) WorkSpace *workspace = WM_window_get_active_workspace(win); const bScreen *screen = BKE_workspace_active_screen_get(win->workspace_hook); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - ARegion *region; rcti rect, overlap_rect; rcti window_rect; @@ -1863,7 +1848,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) area->type = BKE_spacetype_from_id(area->spacetype); } - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { region->type = BKE_regiontype_from_id_or_first(area->type, region->regiontype); } @@ -1887,7 +1872,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) area_azone_init(win, screen, area); /* region windows, default and own handlers */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { region_subwindow(region); if (region->visible) { @@ -2006,7 +1991,6 @@ void ED_region_toggle_hidden(bContext *C, ARegion *region) void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free) { SpaceType *st; - ARegion *region; const char spacetype = area_dst->spacetype; const short flag_copy = HEADER_NO_PULLDOWN; @@ -2026,13 +2010,13 @@ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free) /* regions */ if (do_free) { st = BKE_spacetype_from_id(spacetype); - for (region = area_dst->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area_dst->regionbase) { BKE_area_region_free(st, region); } BLI_freelistN(&area_dst->regionbase); } st = BKE_spacetype_from_id(area_src->spacetype); - for (region = area_src->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area_src->regionbase) { ARegion *newar = BKE_area_region_copy(st, region); BLI_addtail(&area_dst->regionbase, newar); } @@ -2320,7 +2304,6 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi if (area->spacetype != type) { SpaceType *st; SpaceLink *slold = area->spacedata.first; - SpaceLink *sl; /* store area->type->exit callback */ void *area_exit = area->type ? area->type->exit : NULL; /* When the user switches between space-types from the type-selector, @@ -2364,8 +2347,10 @@ void ED_area_newspace(bContext *C, ScrArea *area, int type, const bool skip_regi * (e.g. with properties editor) until space-data is properly created */ /* check previously stored space */ - for (sl = area->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == type) { + SpaceLink *sl = NULL; + LISTBASE_FOREACH (SpaceLink *, sl_iter, &area->spacedata) { + if (sl_iter->spacetype == type) { + sl = sl_iter; break; } } @@ -2541,11 +2526,9 @@ static void region_clear_color(const bContext *C, const ARegion *region, ThemeCo float back[4]; UI_GetThemeColor4fv(colorid, back); GPU_clear_color(back[3] * back[0], back[3] * back[1], back[3] * back[2], back[3]); - GPU_clear(GPU_COLOR_BIT); } else { UI_ThemeClearColor(colorid); - GPU_clear(GPU_COLOR_BIT); } } @@ -2569,14 +2552,12 @@ BLI_INLINE bool streq_array_any(const char *s, const char *arr[]) * correct old \a uiBlock, and NULL otherwise. */ static void ed_panel_draw(const bContext *C, - ScrArea *area, ARegion *region, ListBase *lb, PanelType *pt, Panel *panel, int w, int em, - bool vertical, char *unique_panel_str) { const uiStyle *style = UI_style_get_dpi(); @@ -2592,13 +2573,13 @@ static void ed_panel_draw(const bContext *C, uiBlock *block = UI_block_begin(C, region, block_name, UI_EMBOSS); bool open; - panel = UI_panel_begin(area, region, lb, block, pt, panel, &open); + panel = UI_panel_begin(region, lb, block, pt, panel, &open); /* bad fixed values */ int xco, yco, h = 0; int headerend = w - UI_UNIT_X; - if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { + if (pt->draw_header_preset && !(pt->flag & PNL_NO_HEADER)) { /* for preset menu */ panel->layout = UI_block_layout(block, UI_LAYOUT_HORIZONTAL, @@ -2617,7 +2598,7 @@ static void ed_panel_draw(const bContext *C, panel->layout = NULL; } - if (pt->draw_header && !(pt->flag & PNL_NO_HEADER) && (open || vertical)) { + if (pt->draw_header && !(pt->flag & PNL_NO_HEADER)) { int labelx, labely; UI_panel_label_offset(block, &labelx, &labely); @@ -2694,21 +2675,12 @@ static void ed_panel_draw(const bContext *C, Panel *child_panel = UI_panel_find_by_type(&panel->children, child_pt); if (child_pt->draw && (!child_pt->poll || child_pt->poll(C, child_pt))) { - ed_panel_draw(C, - area, - region, - &panel->children, - child_pt, - child_panel, - w, - em, - vertical, - unique_panel_str); + ed_panel_draw(C, region, &panel->children, child_pt, child_panel, w, em, unique_panel_str); } } } - UI_panel_end(area, region, block, w, h, open); + UI_panel_end(region, block, w, h, open); } /** @@ -2720,14 +2692,12 @@ void ED_region_panels_layout_ex(const bContext *C, ARegion *region, ListBase *paneltypes, const char *contexts[], - int contextnr, - const bool vertical, const char *category_override) { /* collect panels to draw */ WorkSpace *workspace = CTX_wm_workspace(C); LinkNode *panel_types_stack = NULL; - for (PanelType *pt = paneltypes->last; pt; pt = pt->prev) { + LISTBASE_FOREACH_BACKWARD (PanelType *, pt, paneltypes) { /* Only draw top level panels. */ if (pt->parent) { continue; @@ -2772,25 +2742,13 @@ void ED_region_panels_layout_ex(const bContext *C, const int category_tabs_width = UI_PANEL_CATEGORY_MARGIN_WIDTH; int margin_x = 0; const bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE; - const bool is_context_new = (contextnr != -1) ? UI_view2d_tab_set(v2d, contextnr) : false; bool update_tot_size = true; - /* before setting the view */ - if (vertical) { - /* only allow scrolling in vertical direction */ - v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y; - v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X); - v2d->scroll &= ~V2D_SCROLL_BOTTOM; - v2d->scroll |= V2D_SCROLL_RIGHT; - } - else { - /* for now, allow scrolling in both directions (since layouts are optimized for vertical, - * they often don't fit in horizontal layout) - */ - v2d->keepofs &= ~(V2D_LOCKOFS_X | V2D_LOCKOFS_Y | V2D_KEEPOFS_X | V2D_KEEPOFS_Y); - v2d->scroll |= V2D_SCROLL_BOTTOM; - v2d->scroll &= ~V2D_SCROLL_RIGHT; - } + /* only allow scrolling in vertical direction */ + v2d->keepofs |= V2D_LOCKOFS_X | V2D_KEEPOFS_Y; + v2d->keepofs &= ~(V2D_LOCKOFS_Y | V2D_KEEPOFS_X); + v2d->scroll &= ~V2D_SCROLL_BOTTOM; + v2d->scroll |= V2D_SCROLL_RIGHT; /* collect categories */ if (use_category_tabs) { @@ -2815,14 +2773,8 @@ void ED_region_panels_layout_ex(const bContext *C, } } - if (vertical) { - w = BLI_rctf_size_x(&v2d->cur); - em = (region->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */ - } - else { - w = UI_PANEL_WIDTH; - em = (region->type->prefsizex) ? 10 : 20; - } + w = BLI_rctf_size_x(&v2d->cur); + em = (region->type->prefsizex) ? 10 : 20; /* works out to 10*UI_UNIT_X or 20*UI_UNIT_X */ w -= margin_x; int w_box_panel = w - UI_PANEL_BOX_STYLE_MARGIN * 2.0f; @@ -2855,14 +2807,12 @@ void ED_region_panels_layout_ex(const bContext *C, } ed_panel_draw(C, - area, region, ®ion->panels, pt, panel, (pt->flag & PNL_DRAW_BOX) ? w_box_panel : w, em, - vertical, NULL); } @@ -2890,14 +2840,12 @@ void ED_region_panels_layout_ex(const bContext *C, char unique_panel_str[8]; UI_list_panel_unique_str(panel, unique_panel_str); ed_panel_draw(C, - area, region, ®ion->panels, panel->type, panel, (panel->type->flag & PNL_DRAW_BOX) ? w_box_panel : w, em, - vertical, unique_panel_str); } } @@ -2925,7 +2873,7 @@ void ED_region_panels_layout_ex(const bContext *C, y = fabsf(region->sizey * UI_DPI_FAC - 1); } } - else if (vertical) { + else { /* We always keep the scroll offset - * so the total view gets increased with the scrolled away part. */ if (v2d->cur.ymax < -FLT_EPSILON) { @@ -2940,19 +2888,6 @@ void ED_region_panels_layout_ex(const bContext *C, y = -y; } - else { - /* don't jump back when panels close or hide */ - if (!is_context_new) { - if (v2d->tot.xmax > v2d->winx) { - x = max_ii(x, 0); - } - else { - x = max_ii(x, v2d->cur.xmax); - } - } - - y = -y; - } if (update_tot_size) { /* this also changes the 'cur' */ @@ -2966,8 +2901,7 @@ void ED_region_panels_layout_ex(const bContext *C, void ED_region_panels_layout(const bContext *C, ARegion *region) { - bool vertical = true; - ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, NULL, -1, vertical, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, NULL, NULL); } void ED_region_panels_draw(const bContext *C, ARegion *region) @@ -3011,12 +2945,10 @@ void ED_region_panels_draw(const bContext *C, ARegion *region) UI_view2d_scrollers_draw(v2d, mask); } -void ED_region_panels_ex( - const bContext *C, ARegion *region, const char *contexts[], int contextnr, const bool vertical) +void ED_region_panels_ex(const bContext *C, ARegion *region, const char *contexts[]) { /* TODO: remove? */ - ED_region_panels_layout_ex( - C, region, ®ion->type->paneltypes, contexts, contextnr, vertical, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, NULL); ED_region_panels_draw(C, region); } @@ -3042,7 +2974,6 @@ void ED_region_header_layout(const bContext *C, ARegion *region) const uiStyle *style = UI_style_get_dpi(); uiBlock *block; uiLayout *layout; - HeaderType *ht; Header header = {NULL}; bool region_layout_based = region->flag & RGN_FLAG_DYNAMIC_SIZE; @@ -3065,7 +2996,7 @@ void ED_region_header_layout(const bContext *C, ARegion *region) UI_view2d_view_ortho(®ion->v2d); /* draw all headers types */ - for (ht = region->type->headertypes.first; ht; ht = ht->next) { + LISTBASE_FOREACH (HeaderType *, ht, ®ion->type->headertypes) { if (ht->poll && !ht->poll(C, ht)) { continue; } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index c17a34f97b9..3c70bf1bfd8 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -204,7 +204,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } if (CTX_data_equals(member, "visible_bones") || CTX_data_equals(member, "editable_bones")) { bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; - EditBone *ebone, *flipbone = NULL; + EditBone *flipbone = NULL; const bool editable_bones = CTX_data_equals(member, "editable_bones"); if (arm && arm->edbo) { @@ -216,7 +216,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult arm = ob->data; /* Attention: X-Axis Mirroring is also handled here... */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { /* first and foremost, bone must be visible and selected */ if (EBONE_VISIBLE(arm, ebone)) { /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled @@ -262,7 +262,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult if (CTX_data_equals(member, "selected_bones") || CTX_data_equals(member, "selected_editable_bones")) { bArmature *arm = (obedit && obedit->type == OB_ARMATURE) ? obedit->data : NULL; - EditBone *ebone, *flipbone = NULL; + EditBone *flipbone = NULL; const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones"); if (arm && arm->edbo) { @@ -274,7 +274,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult arm = ob->data; /* Attention: X-Axis Mirroring is also handled here... */ - for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { + LISTBASE_FOREACH (EditBone *, ebone, arm->edbo) { /* first and foremost, bone must be visible and selected */ if (EBONE_VISIBLE(arm, ebone) && (ebone->flag & BONE_SELECTED)) { /* Get 'x-axis mirror equivalent' bone if the X-Axis Mirroring option is enabled @@ -479,8 +479,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult if (CTX_data_equals(member, "sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); if (ed) { - Sequence *seq; - for (seq = ed->seqbasep->first; seq; seq = seq->next) { + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); @@ -491,8 +490,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult if (CTX_data_equals(member, "selected_sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); if (ed) { - Sequence *seq; - for (seq = ed->seqbasep->first; seq; seq = seq->next) { + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { if (seq->flag & SELECT) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } @@ -505,8 +503,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult if (CTX_data_equals(member, "selected_editable_sequences")) { Editing *ed = BKE_sequencer_editing_get(scene, false); if (ed) { - Sequence *seq; - for (seq = ed->seqbasep->first; seq; seq = seq->next) { + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { if (seq->flag & SELECT && !(seq->flag & SEQ_LOCK)) { CTX_data_list_add(result, &scene->id, &RNA_Sequence, seq); } @@ -520,16 +517,14 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bAnimContext ac; if (ANIM_animdata_get_context(C, &ac) != 0) { ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; ANIM_animdata_filter(&ac, &anim_data, ANIMFILTER_DATA_VISIBLE, ac.data, ac.datatype); - for (ale = anim_data.first; ale; ale = ale->next) { + LISTBASE_FOREACH (bAnimListElem *, ale, &anim_data) { if (ale->datatype != ALE_NLASTRIP) { continue; } NlaTrack *nlt = (NlaTrack *)ale->data; - NlaStrip *strip; - for (strip = nlt->strips.first; strip; strip = strip->next) { + LISTBASE_FOREACH (NlaStrip *, strip, &nlt->strips) { if (strip->flag & NLASTRIP_FLAG_SELECT) { CTX_data_list_add(result, &scene->id, &RNA_NlaStrip, strip); } @@ -637,9 +632,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { - bGPDlayer *gpl; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if ((gpl->flag & GP_LAYER_HIDE) == 0) { CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl); } @@ -653,9 +646,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bGPdata *gpd = ED_gpencil_data_get_active_direct(area, obact); if (gpd) { - bGPDlayer *gpl; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (BKE_gpencil_layer_is_editable(gpl)) { CTX_data_list_add(result, &gpd->id, &RNA_GPencilLayer, gpl); } @@ -670,12 +661,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); if (gpd) { - bGPDlayer *gpl; - - for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe)) { bGPDframe *gpf; - bGPDstroke *gps; bGPDframe *init_gpf = gpl->actframe; if (is_multiedit) { init_gpf = gpl->frames.first; @@ -683,7 +671,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult for (gpf = init_gpf; gpf; gpf = gpf->next) { if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - for (gps = gpf->strokes.first; gps; gps = gps->next) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { if (ED_gpencil_stroke_can_use_direct(area, gps)) { /* check if the color is editable */ if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) { diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index a5d3c4842e6..8ded845b008 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -379,11 +379,9 @@ void ED_screen_draw_edges(wmWindow *win) float col[4], corner_scale, edge_thickness; int verts_per_corner = 0; - ScrArea *area; - rcti scissor_rect; BLI_rcti_init_minmax(&scissor_rect); - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { BLI_rcti_do_minmax_v(&scissor_rect, (int[2]){area->v1->vec.x, area->v1->vec.y}); BLI_rcti_do_minmax_v(&scissor_rect, (int[2]){area->v3->vec.x, area->v3->vec.y}); } @@ -418,7 +416,7 @@ void ED_screen_draw_edges(wmWindow *win) GPU_batch_uniform_1f(batch, "scale", corner_scale); GPU_batch_uniform_4fv(batch, "color", col); - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { drawscredge_area(area, winsize_x, winsize_y, edge_thickness); } @@ -608,8 +606,8 @@ void ED_screen_preview_render(const bScreen *screen, int size_x, int size_y, uin GPUOffScreen *offscreen = GPU_offscreen_create(size_x, size_y, true, false, err_out); GPU_offscreen_bind(offscreen, true); - GPU_clear_color(0.0, 0.0, 0.0, 0.0); - GPU_clear(GPU_COLOR_BIT | GPU_DEPTH_BIT); + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); + GPU_clear_depth(1.0f); screen_preview_draw(screen, size_x, size_y); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index dbf84cad80b..f534296bd0b 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -230,8 +230,7 @@ bScreen *screen_add(Main *bmain, const char *name, const rcti *rect) void screen_data_copy(bScreen *to, bScreen *from) { - ScrVert *s1, *s2; - ScrEdge *se; + ScrVert *s2; ScrArea *area, *saf; /* free contents of 'to', is from blenkernel screen.c */ @@ -245,11 +244,11 @@ void screen_data_copy(bScreen *to, bScreen *from) BLI_listbase_clear(&to->regionbase); s2 = to->vertbase.first; - for (s1 = from->vertbase.first; s1; s1 = s1->next, s2 = s2->next) { + for (ScrVert *s1 = from->vertbase.first; s1; s1 = s1->next, s2 = s2->next) { s1->newv = s2; } - for (se = to->edgebase.first; se; se = se->next) { + LISTBASE_FOREACH (ScrEdge *, se, &to->edgebase) { se->v1 = se->v1->newv; se->v2 = se->v2->newv; BKE_screen_sort_scrvert(&(se->v1), &(se->v2)); @@ -271,7 +270,7 @@ void screen_data_copy(bScreen *to, bScreen *from) } /* put at zero (needed?) */ - for (s1 = from->vertbase.first; s1; s1 = s1->next) { + LISTBASE_FOREACH (ScrVert *, s1, &from->vertbase) { s1->newv = NULL; } } @@ -538,9 +537,7 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) /* file read, set all screens, ... */ void ED_screens_init(Main *bmain, wmWindowManager *wm) { - wmWindow *win; - - for (win = wm->windows.first; win; win = win->next) { + LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { if (BKE_workspace_active_get(win->workspace_hook) == NULL) { BKE_workspace_active_set(win->workspace_hook, bmain->workspaces.first); } @@ -552,7 +549,7 @@ void ED_screens_init(Main *bmain, wmWindowManager *wm) } if (U.uiflag & USER_HEADER_FROM_PREF) { - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { BKE_screen_header_alignment_reset(screen); } } @@ -614,7 +611,6 @@ void ED_area_exit(bContext *C, ScrArea *area) wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); ScrArea *prevsa = CTX_wm_area(C); - ARegion *region; if (area->type && area->type->exit) { area->type->exit(wm, area); @@ -622,7 +618,7 @@ void ED_area_exit(bContext *C, ScrArea *area) CTX_wm_area_set(C, area); - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { ED_region_exit(C, region); } @@ -683,10 +679,11 @@ static void screen_cursor_set(wmWindow *win, const int xy[2]) { const bScreen *screen = WM_window_get_active_screen(win); AZone *az = NULL; - ScrArea *area; + ScrArea *area = NULL; - for (area = screen->areabase.first; area; area = area->next) { - if ((az = ED_area_actionzone_find_xy(area, xy))) { + LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { + if ((az = ED_area_actionzone_find_xy(area_iter, xy))) { + area = area_iter; break; } } @@ -733,7 +730,6 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } ScrArea *area = NULL; - ARegion *region; ARegion *region_prev = screen->active_region; ED_screen_areas_iter (win, screen, area_iter) { @@ -750,7 +746,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } if (area) { /* Make overlap active when mouse over. */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (ED_region_contains_xy(region, xy)) { screen->active_region = region; break; @@ -767,8 +763,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) ED_screen_areas_iter (win, screen, area_iter) { bool do_draw = false; - for (region = area_iter->regionbase.first; region; region = region->next) { - + LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) { /* Call old area's deactivate if assigned. */ if (region == region_prev && area_iter->type->deactivate) { area_iter->type->deactivate(area_iter); @@ -789,7 +784,7 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } if (do_draw) { - for (region = area_iter->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) { if (ELEM(region->regiontype, RGN_TYPE_HEADER, RGN_TYPE_TOOL_HEADER)) { ED_region_tag_redraw_no_rebuild(region); } @@ -826,13 +821,12 @@ int ED_screen_area_active(const bContext *C) if (win && screen && area) { AZone *az = ED_area_actionzone_find_xy(area, &win->eventstate->x); - ARegion *region; if (az && az->type == AZONE_REGION) { return 1; } - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region == screen->active_region) { return 1; } @@ -883,10 +877,10 @@ static void screen_global_area_refresh(wmWindow *win, const short height_min, const short height_max) { - ScrArea *area; - - for (area = win->global_areas.areabase.first; area; area = area->next) { - if (area->spacetype == space_type) { + ScrArea *area = NULL; + LISTBASE_FOREACH (ScrArea *, area_iter, &win->global_areas.areabase) { + if (area_iter->spacetype == space_type) { + area = area_iter; break; } } @@ -1081,7 +1075,6 @@ static void screen_set_3dview_camera(Scene *scene, v3d->camera = BKE_view_layer_camera_find(view_layer); // XXX if (screen == curscreen) handle_view3d_lock(); if (!v3d->camera) { - ARegion *region; ListBase *regionbase; /* regionbase is in different place depending if space is active */ @@ -1092,7 +1085,7 @@ static void screen_set_3dview_camera(Scene *scene, regionbase = &v3d->regionbase; } - for (region = regionbase->first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = region->regiondata; if (rv3d->persp == RV3D_CAMOB) { @@ -1240,13 +1233,12 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const wmWindowManager *wm = CTX_wm_manager(C); WorkSpace *workspace = WM_window_get_active_workspace(win); bScreen *screen, *oldscreen; - ARegion *region; if (area) { /* ensure we don't have a button active anymore, can crash when * switching screens with tooltip open because region and tooltip * are no longer in the same screen */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { UI_blocklist_free(C, ®ion->uiblocks); if (region->regiontimer) { @@ -1299,7 +1291,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const glob_area->global->flag &= ~GLOBAL_AREA_IS_HIDDEN; } /* restore the old side panels/header visibility */ - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { region->flag = region->flagfullscreen; } } @@ -1364,7 +1356,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *area, const glob_area->global->flag |= GLOBAL_AREA_IS_HIDDEN; } /* temporarily hide the side panels/header */ - for (region = newa->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &newa->regionbase) { region->flagfullscreen = region->flag; if (ELEM(region->regiontype, @@ -1537,13 +1529,11 @@ void ED_screen_animation_timer(bContext *C, int redraws, int sync, int enable) static ARegion *time_top_left_3dwindow(bScreen *screen) { ARegion *aret = NULL; - ScrArea *area; int min = 10000; - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacetype == SPACE_VIEW3D) { - ARegion *region; - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiontype == RGN_TYPE_WINDOW) { if (region->winrct.xmin - region->winrct.ymin < min) { aret = region; @@ -1576,15 +1566,14 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph) { Scene *scene = DEG_get_input_scene(depsgraph); - DEG_id_tag_update_ex(bmain, &scene->id, ID_RECALC_TIME); + DEG_time_tag_update(bmain); #ifdef DURIAN_CAMERA_SWITCH void *camera = BKE_scene_camera_switch_find(scene); if (camera && scene->camera != camera) { - bScreen *screen; scene->camera = camera; /* are there cameras in the views that are not in the scene? */ - for (screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { BKE_screen_view3d_scene_sync(screen, scene); } DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); @@ -1602,10 +1591,9 @@ void ED_update_for_newframe(Main *bmain, Depsgraph *depsgraph) */ bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene) { - ScrArea *area; const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { switch (area->spacetype) { case SPACE_VIEW3D: { View3D *v3d; @@ -1616,8 +1604,7 @@ bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene) v3d = area->spacedata.first; if (v3d->camera && v3d->stereo3d_camera == STEREO_3D_ID) { - ARegion *region; - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region->regiondata && region->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d = region->regiondata; if (rv3d->persp == RV3D_CAMOB) { diff --git a/source/blender/editors/screen/screen_geometry.c b/source/blender/editors/screen/screen_geometry.c index 0b83a657265..4917dfa5e69 100644 --- a/source/blender/editors/screen/screen_geometry.c +++ b/source/blender/editors/screen/screen_geometry.c @@ -162,7 +162,6 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) const int screen_size_x = BLI_rcti_size_x(&screen_rect); const int screen_size_y = BLI_rcti_size_y(&screen_rect); - ScrVert *sv = NULL; int screen_size_x_prev, screen_size_y_prev; float min[2], max[2]; @@ -170,7 +169,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) min[0] = min[1] = 20000.0f; max[0] = max[1] = 0.0f; - for (sv = screen->vertbase.first; sv; sv = sv->next) { + LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) { const float fv[2] = {(float)sv->vec.x, (float)sv->vec.y}; minmax_v2v2_v2(min, max, fv); } @@ -183,7 +182,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) const float facy = ((float)screen_size_y - 1) / ((float)screen_size_y_prev - 1); /* make sure it fits! */ - for (sv = screen->vertbase.first; sv; sv = sv->next) { + LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) { sv->vec.x = screen_rect.xmin + round_fl_to_short((sv->vec.x - min[0]) * facx); CLAMP(sv->vec.x, screen_rect.xmin, screen_rect.xmax - 1); @@ -208,7 +207,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) screen_geom_select_connected_edge(win, se); /* all selected vertices get the right offset */ - for (sv = screen->vertbase.first; sv; sv = sv->next) { + LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) { /* if is a collapsed area */ if (sv != area->v1 && sv != area->v4) { if (sv->flag) { @@ -232,7 +231,7 @@ void screen_geom_vertices_scale(const wmWindow *win, bScreen *screen) screen_geom_select_connected_edge(win, se); /* all selected vertices get the right offset */ - for (sv = screen->vertbase.first; sv; sv = sv->next) { + LISTBASE_FOREACH (ScrVert *, sv, &screen->vertbase) { /* if is not a collapsed area */ if (sv != area->v2 && sv != area->v3) { if (sv->flag) { diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index b002b23a7f3..aff19ddacf1 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -692,10 +692,9 @@ static bool actionzone_area_poll(bContext *C) if (screen && win && win->eventstate) { const int *xy = &win->eventstate->x; - AZone *az; LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - for (az = area->actionzones.first; az; az = az->next) { + LISTBASE_FOREACH (AZone *, az, &area->actionzones) { if (BLI_rcti_isect_pt_v(&az->rect, xy)) { return 1; } @@ -3068,13 +3067,12 @@ static void SCREEN_OT_keyframe_jump(wmOperatorType *ot) static int marker_jump_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - TimeMarker *marker; int closest = CFRA; const bool next = RNA_boolean_get(op->ptr, "next"); bool found = false; /* find matching marker in the right direction */ - for (marker = scene->markers.first; marker; marker = marker->next) { + LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { if (next) { if ((marker->frame > CFRA) && (!found || closest > marker->frame)) { closest = marker->frame; @@ -3170,8 +3168,9 @@ static int screen_maximize_area_exec(bContext *C, wmOperator *op) /* search current screen for 'fullscreen' areas */ /* prevents restoring info header, when mouse is over it */ - for (area = screen->areabase.first; area; area = area->next) { - if (area->full) { + LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { + if (area_iter->full) { + area = area_iter; break; } } @@ -3619,12 +3618,10 @@ static void SCREEN_OT_area_options(wmOperatorType *ot) static int spacedata_cleanup_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); - bScreen *screen; - ScrArea *area; int tot = 0; - for (screen = bmain->screens.first; screen; screen = screen->id.next) { - for (area = screen->areabase.first; area; area = area->next) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacedata.first != area->spacedata.last) { SpaceLink *sl = area->spacedata.first; @@ -3854,7 +3851,6 @@ static int region_quadview_exec(bContext *C, wmOperator *op) region->alignment = 0; if (area->spacetype == SPACE_VIEW3D) { - ARegion *region_iter; RegionView3D *rv3d = region->regiondata; /* if this is a locked view, use settings from 'User' view */ @@ -3878,7 +3874,7 @@ static int region_quadview_exec(bContext *C, wmOperator *op) rv3d->rflag |= RV3D_GPULIGHT_UPDATE; /* Accumulate locks, in case they're mixed. */ - for (region_iter = area->regionbase.first; region_iter; region_iter = region_iter->next) { + LISTBASE_FOREACH (ARegion *, region_iter, &area->regionbase) { if (region_iter->regiontype == RGN_TYPE_WINDOW) { RegionView3D *rv3d_iter = region_iter->regiondata; rv3d->viewlock_quad |= rv3d_iter->viewlock; @@ -4436,13 +4432,11 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); Scene *scene_eval = (depsgraph != NULL) ? DEG_get_evaluated_scene(depsgraph) : NULL; wmTimer *wt = screen->animtimer; ScreenAnimData *sad = wt->customdata; wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *window; - ScrArea *area; int sync; double time; @@ -4588,12 +4582,11 @@ static int screen_animation_step(bContext *C, wmOperator *UNUSED(op), const wmEv ED_update_for_newframe(bmain, depsgraph); } - for (window = wm->windows.first; window; window = window->next) { + LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { const bScreen *win_screen = WM_window_get_active_screen(window); - for (area = win_screen->areabase.first; area; area = area->next) { - ARegion *region; - for (region = area->regionbase.first; region; region = region->next) { + LISTBASE_FOREACH (ScrArea *, area, &win_screen->areabase) { + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { bool redraw = false; if (region == sad->region) { redraw = true; @@ -4867,8 +4860,9 @@ static int fullscreen_back_exec(bContext *C, wmOperator *op) ScrArea *area = NULL; /* search current screen for 'fullscreen' areas */ - for (area = screen->areabase.first; area; area = area->next) { - if (area->full) { + LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { + if (area_iter->full) { + area = area_iter; break; } } diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index b20dc80d158..702c824077d 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -409,8 +409,7 @@ static void workspace_add_menu(bContext *UNUSED(C), uiLayout *layout, void *temp WorkspaceConfigFileData *builtin_config = workspace_system_file_read(app_template); if (startup_config) { - for (WorkSpace *workspace = startup_config->workspaces.first; workspace; - workspace = workspace->id.next) { + LISTBASE_FOREACH (WorkSpace *, workspace, &startup_config->workspaces) { uiLayout *row = uiLayoutRow(layout, false); workspace_append_button(row, ot_append, workspace, startup_config->main); has_startup_items = true; @@ -420,8 +419,7 @@ static void workspace_add_menu(bContext *UNUSED(C), uiLayout *layout, void *temp if (builtin_config) { bool has_title = false; - for (WorkSpace *workspace = builtin_config->workspaces.first; workspace; - workspace = workspace->id.next) { + LISTBASE_FOREACH (WorkSpace *, workspace, &builtin_config->workspaces) { if (startup_config && BLI_findstring(&startup_config->workspaces, workspace->id.name, offsetof(ID, name))) { continue; diff --git a/source/blender/editors/screen/workspace_layout_edit.c b/source/blender/editors/screen/workspace_layout_edit.c index 8a36cffa1f1..f4b076aca00 100644 --- a/source/blender/editors/screen/workspace_layout_edit.c +++ b/source/blender/editors/screen/workspace_layout_edit.c @@ -168,8 +168,7 @@ static bool workspace_change_find_new_layout_cb(const WorkSpaceLayout *layout, v static bScreen *screen_fullscreen_find_associated_normal_screen(const Main *bmain, bScreen *screen) { - for (bScreen *screen_iter = bmain->screens.first; screen_iter; - screen_iter = screen_iter->id.next) { + LISTBASE_FOREACH (bScreen *, screen_iter, &bmain->screens) { if ((screen_iter != screen) && ELEM(screen_iter->state, SCREENMAXIMIZED, SCREENFULL)) { ScrArea *area = screen_iter->areabase.first; if (area && area->full == screen) { diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index 0e38340d3bc..ee514fa745c 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -566,7 +566,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, if (load_tex(brush, vc, zoom, col, primary)) { GPU_color_mask(true, true, true, true); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { GPU_matrix_push(); @@ -693,7 +693,7 @@ static bool paint_draw_cursor_overlay( float center[2]; GPU_color_mask(true, true, true, true); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (ups->draw_anchored) { copy_v2_v2(center, ups->anchored_initial_mouse); @@ -776,7 +776,7 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups, ePaintOverlayControlFlags flags = BKE_paint_get_overlay_flags(); eGPUBlend blend_state = GPU_blend_get(); - bool depth_test = GPU_depth_test_enabled(); + eGPUDepthTest depth_test = GPU_depth_test_get(); /* Translate to region. */ GPU_matrix_push(); @@ -1147,9 +1147,9 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, immUniformColor4f(1.0f, 1.0f, 1.0f, 0.6f); /* Cursor normally draws on top, but for this part we need depth tests. */ - const bool depth_test = GPU_depth_test_enabled(); + const eGPUDepthTest depth_test = GPU_depth_test_get(); if (!depth_test) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } GPU_line_width(1.0f); @@ -1163,7 +1163,7 @@ static void sculpt_geometry_preview_lines_draw(const uint gpuattr, /* Restore depth test value. */ if (!depth_test) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } } diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index 05ffb80d8a1..ab8b81a8155 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -31,6 +31,7 @@ #include "BLI_lasso_2d.h" #include "BLI_math_geom.h" #include "BLI_math_matrix.h" +#include "BLI_rect.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -221,392 +222,383 @@ void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot) 1.0f); } -/* Box select, operator is VIEW3D_OT_select_box, defined in view3d_select.c. */ +/* Sculpt Gesture Operators. */ -static bool is_effected(float planes[4][4], const float co[3]) -{ - return isect_point_planes_v3(planes, 4, co); -} +typedef enum eSculptGestureShapeType { + SCULPT_GESTURE_SHAPE_BOX, + SCULPT_GESTURE_SHAPE_LASSO, +} eMaskGesturesShapeType; -static void flip_plane(float out[4], const float in[4], const char symm) -{ - if (symm & PAINT_SYMM_X) { - out[0] = -in[0]; - } - else { - out[0] = in[0]; - } - if (symm & PAINT_SYMM_Y) { - out[1] = -in[1]; - } - else { - out[1] = in[1]; - } - if (symm & PAINT_SYMM_Z) { - out[2] = -in[2]; - } - else { - out[2] = in[2]; - } +typedef struct LassoGestureData { + float projviewobjmat[4][4]; - out[3] = in[3]; -} + rcti boundbox; + int width; -static void mask_box_select_task_cb(void *__restrict userdata, - const int i, - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - MaskTaskData *data = userdata; + /* 2D bitmap to test if a vertex is affected by the lasso shape. */ + BLI_bitmap *mask_px; +} LassoGestureData; - PBVHNode *node = data->nodes[i]; +typedef struct SculptGestureContext { + SculptSession *ss; + ViewContext vc; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; - float(*clip_planes_final)[4] = data->clip_planes_final; + /* Enabled and currently active symmetry. */ + ePaintSymmetryFlags symm; + ePaintSymmetryFlags symmpass; - PBVHVertexIter vi; - bool any_masked = false; - bool redraw = false; + /* Operation parameters. */ + eMaskGesturesShapeType shape_type; + bool front_faces_only; - float vertex_normal[3]; + /* Mask operation parameters. */ + PaintMaskFloodMode mask_mode; + float mask_value; - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) - { - SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal); - float dot = dot_v3v3(data->view_normal, vertex_normal); - const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f); + /* View parameters. */ + float true_view_normal[3]; + float view_normal[3]; - if (is_effected_front_face && is_effected(clip_planes_final, vi.co)) { - float prevmask = *vi.mask; - if (!any_masked) { - any_masked = true; + float true_clip_planes[4][4]; + float clip_planes[4][4]; - SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + /* Lasso Gesture. */ + LassoGestureData lasso; - if (data->multires) { - BKE_pbvh_node_mark_normals_update(node); - } - } - mask_flood_fill_set_elem(vi.mask, mode, value); - if (prevmask != *vi.mask) { - redraw = true; - } - } - } - BKE_pbvh_vertex_iter_end; + /* Task Callback Data. */ + PBVHNode **nodes; + int totnode; +} SculptGestureContext; - if (redraw) { - BKE_pbvh_node_mark_update_mask(node); - } +static void sculpt_gesture_operator_properties(wmOperatorType *ot) +{ + RNA_def_boolean(ot->srna, + "use_front_faces_only", + false, + "Front Faces Only", + "Affect only faces facing towards the view"); } -static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) +static void sculpt_gesture_context_init_common(bContext *C, + wmOperator *op, + SculptGestureContext *sgcontext) { - ViewContext vc; Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ED_view3d_viewcontext_init(C, &vc, depsgraph); - - Sculpt *sd = vc.scene->toolsettings->sculpt; - BoundBox bb; - float clip_planes[4][4]; - float clip_planes_final[4][4]; - ARegion *region = vc.region; - Object *ob = vc.obact; - bool multires; - PBVH *pbvh; - PBVHNode **nodes; - int totnode; - int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - - const PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); - const float value = RNA_float_get(op->ptr, "value"); - const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); + ED_view3d_viewcontext_init(C, &sgcontext->vc, depsgraph); - rcti rect; - WM_operator_properties_border_to_rcti(op, &rect); + Sculpt *sd = sgcontext->vc.scene->toolsettings->sculpt; + Object *ob = sgcontext->vc.obact; - /* Transform the clip planes in object space. */ - ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &rect); + /* Operator properties. */ + sgcontext->front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); + /* SculptSession */ + sgcontext->ss = ob->sculpt; - SCULPT_undo_push_begin("Mask box fill"); + /* Symmetry. */ + sgcontext->symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - /* Calculate the view normal in object space. */ + /* View Normal. */ float mat[3][3]; float view_dir[3] = {0.0f, 0.0f, 1.0f}; - float true_view_normal[3]; - copy_m3_m4(mat, vc.rv3d->viewinv); + copy_m3_m4(mat, sgcontext->vc.rv3d->viewinv); mul_m3_v3(mat, view_dir); copy_m3_m4(mat, ob->imat); mul_m3_v3(mat, view_dir); - normalize_v3_v3(true_view_normal, view_dir); + normalize_v3_v3(sgcontext->true_view_normal, view_dir); +} - for (int symmpass = 0; symmpass <= symm; symmpass++) { - if (symmpass == 0 || (symm & symmpass && (symm != 5 || symmpass != 3) && - (symm != 6 || (symmpass != 3 && symmpass != 5)))) { +static void sculpt_gesture_lasso_px_cb(int x, int x_end, int y, void *user_data) +{ + SculptGestureContext *mcontext = user_data; + LassoGestureData *lasso = &mcontext->lasso; + int index = (y * lasso->width) + x; + int index_end = (y * lasso->width) + x_end; + do { + BLI_BITMAP_ENABLE(lasso->mask_px, index); + } while (++index != index_end); +} - /* Flip the planes symmetrically as needed. */ - for (int j = 0; j < 4; j++) { - flip_plane(clip_planes_final[j], clip_planes[j], symmpass); - } +static SculptGestureContext *sculpt_gesture_init_from_lasso(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext), + "sculpt gesture context lasso"); + sgcontext->shape_type = SCULPT_GESTURE_SHAPE_LASSO; + + sculpt_gesture_context_init_common(C, op, sgcontext); - PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4}; - BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); + int mcoords_len; + const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); - negate_m4(clip_planes_final); + if (!mcoords) { + return NULL; + } - MaskTaskData data = { - .ob = ob, - .pbvh = pbvh, - .nodes = nodes, - .multires = multires, - .mode = mode, - .value = value, - .clip_planes_final = clip_planes_final, - .front_faces_only = front_faces_only, - }; + ED_view3d_ob_project_mat_get( + sgcontext->vc.rv3d, sgcontext->vc.obact, sgcontext->lasso.projviewobjmat); + BLI_lasso_boundbox(&sgcontext->lasso.boundbox, mcoords, mcoords_len); + sgcontext->lasso.width = sgcontext->lasso.boundbox.xmax - sgcontext->lasso.boundbox.xmin; + sgcontext->lasso.mask_px = BLI_BITMAP_NEW( + sgcontext->lasso.width * (sgcontext->lasso.boundbox.ymax - sgcontext->lasso.boundbox.ymin), + __func__); + + BLI_bitmap_draw_2d_poly_v2i_n(sgcontext->lasso.boundbox.xmin, + sgcontext->lasso.boundbox.ymin, + sgcontext->lasso.boundbox.xmax, + sgcontext->lasso.boundbox.ymax, + mcoords, + mcoords_len, + sculpt_gesture_lasso_px_cb, + sgcontext); - flip_v3_v3(data.view_normal, true_view_normal, symmpass); + BoundBox bb; + ED_view3d_clipping_calc(&bb, + sgcontext->true_clip_planes, + sgcontext->vc.region, + sgcontext->vc.obact, + &sgcontext->lasso.boundbox); + MEM_freeN((void *)mcoords); + + return sgcontext; +} - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, mask_box_select_task_cb, &settings); +static SculptGestureContext *sculpt_gesture_init_from_box(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = MEM_callocN(sizeof(SculptGestureContext), + "sculpt gesture context box"); + sgcontext->shape_type = SCULPT_GESTURE_SHAPE_BOX; - if (nodes) { - MEM_freeN(nodes); - } - } - } + sculpt_gesture_context_init_common(C, op, sgcontext); - if (multires) { - multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); - } + rcti rect; + WM_operator_properties_border_to_rcti(op, &rect); - BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + BoundBox bb; + ED_view3d_clipping_calc( + &bb, sgcontext->true_clip_planes, sgcontext->vc.region, sgcontext->vc.obact, &rect); - SCULPT_undo_push_end(); + return sgcontext; +} - ED_region_tag_redraw(region); +static void sculpt_gesture_context_free(SculptGestureContext *sgcontext) +{ + MEM_SAFE_FREE(sgcontext->lasso.mask_px); + MEM_SAFE_FREE(sgcontext->nodes); + MEM_SAFE_FREE(sgcontext); +} - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); +static void flip_plane(float out[4], const float in[4], const char symm) +{ + if (symm & PAINT_SYMM_X) { + out[0] = -in[0]; + } + else { + out[0] = in[0]; + } + if (symm & PAINT_SYMM_Y) { + out[1] = -in[1]; + } + else { + out[1] = in[1]; + } + if (symm & PAINT_SYMM_Z) { + out[2] = -in[2]; + } + else { + out[2] = in[2]; + } - return true; + out[3] = in[3]; } -typedef struct LassoMaskData { - struct ViewContext *vc; - float projviewobjmat[4][4]; - BLI_bitmap *px; - int width; - /* Bounding box for scanfilling. */ - rcti rect; - int symmpass; +static void sculpt_gesture_flip_for_symmetry_pass(SculptGestureContext *sgcontext, + const ePaintSymmetryFlags symmpass) +{ + sgcontext->symmpass = symmpass; + for (int j = 0; j < 4; j++) { + flip_plane(sgcontext->clip_planes[j], sgcontext->true_clip_planes[j], symmpass); + } + negate_m4(sgcontext->clip_planes); + flip_v3_v3(sgcontext->view_normal, sgcontext->true_view_normal, symmpass); +} - MaskTaskData task_data; -} LassoMaskData; +static void sculpt_gesture_update_effected_nodes(SculptGestureContext *sgcontext) +{ + SculptSession *ss = sgcontext->ss; + float clip_planes[4][4]; + copy_m4_m4(clip_planes, sgcontext->clip_planes); + negate_m4(clip_planes); + PBVHFrustumPlanes frustum = {.planes = clip_planes, .num_planes = 4}; + BKE_pbvh_search_gather(ss->pbvh, + BKE_pbvh_node_frustum_contain_AABB, + &frustum, + &sgcontext->nodes, + &sgcontext->totnode); +} -/** - * Lasso select. This could be defined as part of #VIEW3D_OT_select_lasso, - * still the shortcuts conflict, so we will use a separate operator. - */ -static bool is_effected_lasso(LassoMaskData *data, const float co[3]) +static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, const float co[3]) { float scr_co_f[2]; int scr_co_s[2]; float co_final[3]; - flip_v3_v3(co_final, co, data->symmpass); + flip_v3_v3(co_final, co, sgcontext->symmpass); + /* First project point to 2d space. */ - ED_view3d_project_float_v2_m4(data->vc->region, co_final, scr_co_f, data->projviewobjmat); + ED_view3d_project_float_v2_m4( + sgcontext->vc.region, co_final, scr_co_f, sgcontext->lasso.projviewobjmat); scr_co_s[0] = scr_co_f[0]; scr_co_s[1] = scr_co_f[1]; - /* Clip against screen, because lasso is limited to screen only. */ - if ((scr_co_s[0] < data->rect.xmin) || (scr_co_s[1] < data->rect.ymin) || - (scr_co_s[0] >= data->rect.xmax) || (scr_co_s[1] >= data->rect.ymax)) { + /* Clip against lasso boundbox. */ + LassoGestureData *lasso = &sgcontext->lasso; + if (!BLI_rcti_isect_pt(&lasso->boundbox, scr_co_s[0], scr_co_s[1])) { return false; } - scr_co_s[0] -= data->rect.xmin; - scr_co_s[1] -= data->rect.ymin; + scr_co_s[0] -= lasso->boundbox.xmin; + scr_co_s[1] -= lasso->boundbox.ymin; - return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]); + return BLI_BITMAP_TEST_BOOL(lasso->mask_px, scr_co_s[1] * lasso->width + scr_co_s[0]); } -static void mask_lasso_px_cb(int x, int x_end, int y, void *user_data) +static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd) { - LassoMaskData *data = user_data; - int index = (y * data->width) + x; - int index_end = (y * data->width) + x_end; - do { - BLI_BITMAP_ENABLE(data->px, index); - } while (++index != index_end); + float vertex_normal[3]; + SCULPT_vertex_normal_get(sgcontext->ss, vd->index, vertex_normal); + float dot = dot_v3v3(sgcontext->view_normal, vertex_normal); + const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f); + + if (!is_effected_front_face) { + return false; + } + + switch (sgcontext->shape_type) { + case SCULPT_GESTURE_SHAPE_BOX: + return isect_point_planes_v3(sgcontext->clip_planes, 4, vd->co); + case SCULPT_GESTURE_SHAPE_LASSO: + return sculpt_gesture_is_effected_lasso(sgcontext, vd->co); + } + return false; } -static void mask_gesture_lasso_task_cb(void *__restrict userdata, +static void mask_gesture_apply_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) { - LassoMaskData *lasso_data = userdata; - MaskTaskData *data = &lasso_data->task_data; - - PBVHNode *node = data->nodes[i]; + SculptGestureContext *sgcontext = userdata; + Object *ob = sgcontext->vc.obact; + PBVHNode *node = sgcontext->nodes[i]; - const PaintMaskFloodMode mode = data->mode; - const float value = data->value; + const bool is_multires = BKE_pbvh_type(sgcontext->ss->pbvh) == PBVH_GRIDS; - PBVHVertexIter vi; + PBVHVertexIter vd; bool any_masked = false; + bool redraw = false; - float vertex_normal[3]; - - BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) + BKE_pbvh_vertex_iter_begin(sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - SCULPT_vertex_normal_get(data->ob->sculpt, vi.index, vertex_normal); - float dot = dot_v3v3(lasso_data->task_data.view_normal, vertex_normal); - const bool is_effected_front_face = !(data->front_faces_only && dot < 0.0f); - - if (is_effected_front_face && is_effected_lasso(lasso_data, vi.co)) { + if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { + float prevmask = *vd.mask; if (!any_masked) { any_masked = true; - SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_MASK); + SCULPT_undo_push_node(ob, node, SCULPT_UNDO_MASK); - BKE_pbvh_node_mark_redraw(node); - if (data->multires) { + if (is_multires) { BKE_pbvh_node_mark_normals_update(node); } } - - mask_flood_fill_set_elem(vi.mask, mode, value); + mask_flood_fill_set_elem(vd.mask, sgcontext->mask_mode, sgcontext->mask_value); + if (prevmask != *vd.mask) { + redraw = true; + } } } BKE_pbvh_vertex_iter_end; + + if (redraw) { + BKE_pbvh_node_mark_update_mask(node); + } } -static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) +static void sculpt_gesture_apply(bContext *C, SculptGestureContext *mcontext) { - int mcoords_len; - const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len); + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + BKE_sculpt_update_object_for_edit(depsgraph, mcontext->vc.obact, false, true, false); - if (mcoords) { - Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - float clip_planes[4][4], clip_planes_final[4][4]; - BoundBox bb; - Object *ob; - ViewContext vc; - LassoMaskData data; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - PBVH *pbvh; - PBVHNode **nodes; - int totnode; - bool multires; - PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode"); - float value = RNA_float_get(op->ptr, "value"); - const bool front_faces_only = RNA_boolean_get(op->ptr, "use_front_faces_only"); - - /* Calculations of individual vertices are done in 2D screen space to diminish the amount of - * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle - * of lasso. */ - ED_view3d_viewcontext_init(C, &vc, depsgraph); - - /* Lasso data calculations. */ - data.vc = &vc; - ob = vc.obact; - ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat); - - BLI_lasso_boundbox(&data.rect, mcoords, mcoords_len); - data.width = data.rect.xmax - data.rect.xmin; - data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__); - - BLI_bitmap_draw_2d_poly_v2i_n(data.rect.xmin, - data.rect.ymin, - data.rect.xmax, - data.rect.ymax, - mcoords, - mcoords_len, - mask_lasso_px_cb, - &data); - - ED_view3d_clipping_calc(&bb, clip_planes, vc.region, vc.obact, &data.rect); - - BKE_sculpt_update_object_for_edit(depsgraph, ob, false, true, false); - pbvh = ob->sculpt->pbvh; - multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS); - - SCULPT_undo_push_begin("Mask lasso fill"); - - /* Calculate the view normal in object space. */ - float mat[3][3]; - float view_dir[3] = {0.0f, 0.0f, 1.0f}; - float true_view_normal[3]; - copy_m3_m4(mat, vc.rv3d->viewinv); - mul_m3_v3(mat, view_dir); - copy_m3_m4(mat, ob->imat); - mul_m3_v3(mat, view_dir); - normalize_v3_v3(true_view_normal, view_dir); - - for (int symmpass = 0; symmpass <= symm; symmpass++) { - if ((symmpass == 0) || (symm & symmpass && (symm != 5 || symmpass != 3) && - (symm != 6 || (symmpass != 3 && symmpass != 5)))) { - - /* Flip the planes symmetrically as needed. */ - for (int j = 0; j < 4; j++) { - flip_plane(clip_planes_final[j], clip_planes[j], symmpass); - } + SCULPT_undo_push_begin("Sculpt Gesture Apply"); - flip_v3_v3(data.task_data.view_normal, true_view_normal, symmpass); + for (ePaintSymmetryFlags symmpass = 0; symmpass <= mcontext->symm; symmpass++) { + if (SCULPT_is_symmetry_iteration_valid(symmpass, mcontext->symm)) { + sculpt_gesture_flip_for_symmetry_pass(mcontext, symmpass); + sculpt_gesture_update_effected_nodes(mcontext); - data.symmpass = symmpass; - - /* Gather nodes inside lasso's enclosing rectangle - * (should greatly help with bigger meshes). */ - PBVHFrustumPlanes frustum = {.planes = clip_planes_final, .num_planes = 4}; - BKE_pbvh_search_gather( - pbvh, BKE_pbvh_node_frustum_contain_AABB, &frustum, &nodes, &totnode); - - negate_m4(clip_planes_final); + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, mcontext->totnode); + BLI_task_parallel_range( + 0, mcontext->totnode, mcontext, mask_gesture_apply_task_cb, &settings); - data.task_data.ob = ob; - data.task_data.pbvh = pbvh; - data.task_data.nodes = nodes; - data.task_data.multires = multires; - data.task_data.mode = mode; - data.task_data.value = value; - data.task_data.front_faces_only = front_faces_only; + MEM_SAFE_FREE(mcontext->nodes); + } + } - TaskParallelSettings settings; - BKE_pbvh_parallel_range_settings(&settings, true, totnode); - BLI_task_parallel_range(0, totnode, &data, mask_gesture_lasso_task_cb, &settings); + if (BKE_pbvh_type(mcontext->ss->pbvh) == PBVH_GRIDS) { + multires_mark_as_modified(depsgraph, mcontext->vc.obact, MULTIRES_COORDS_MODIFIED); + } - if (nodes) { - MEM_freeN(nodes); - } - } - } + BKE_pbvh_update_vertex_data(mcontext->ss->pbvh, PBVH_UpdateMask); - if (multires) { - multires_mark_as_modified(depsgraph, ob, MULTIRES_COORDS_MODIFIED); - } + SCULPT_undo_push_end(); - BKE_pbvh_update_vertex_data(pbvh, PBVH_UpdateMask); + ED_region_tag_redraw(mcontext->vc.region); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, mcontext->vc.obact); +} - SCULPT_undo_push_end(); +static void sculpt_gesture_init_mask_properties(SculptGestureContext *sgcontext, wmOperator *op) +{ + sgcontext->mask_mode = RNA_enum_get(op->ptr, "mode"); + sgcontext->mask_value = RNA_float_get(op->ptr, "value"); +} - ED_region_tag_redraw(vc.region); - MEM_freeN((void *)mcoords); - MEM_freeN(data.px); +static void paint_mask_gesture_operator_properties(wmOperatorType *ot) +{ + RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); + RNA_def_float( + ot->srna, + "value", + 1.0f, + 0.0f, + 1.0f, + "Value", + "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", + 0.0f, + 1.0f); +} - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); +static int paint_mask_gesture_box_exec(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = sculpt_gesture_init_from_box(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; + } + sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; +} - return OPERATOR_FINISHED; +static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op) +{ + SculptGestureContext *sgcontext = sculpt_gesture_init_from_lasso(C, op); + if (!sgcontext) { + return OPERATOR_CANCELLED; } - return OPERATOR_PASS_THROUGH; + sculpt_gesture_init_mask_properties(sgcontext, op); + sculpt_gesture_apply(C, sgcontext); + sculpt_gesture_context_free(sgcontext); + return OPERATOR_FINISHED; } void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) @@ -625,23 +617,9 @@ void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot) /* Properties. */ WM_operator_properties_gesture_lasso(ot); + sculpt_gesture_operator_properties(ot); - RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float( - ot->srna, - "value", - 1.0f, - 0.0f, - 1.0f, - "Value", - "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", - 0.0f, - 1.0f); - RNA_def_boolean(ot->srna, - "use_front_faces_only", - false, - "Front Faces Only", - "Affect only faces facing towards the view"); + paint_mask_gesture_operator_properties(ot); } void PAINT_OT_mask_box_gesture(wmOperatorType *ot) @@ -660,21 +638,7 @@ void PAINT_OT_mask_box_gesture(wmOperatorType *ot) /* Properties. */ WM_operator_properties_border(ot); + sculpt_gesture_operator_properties(ot); - RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL); - RNA_def_float( - ot->srna, - "value", - 1.0f, - 0.0f, - 1.0f, - "Value", - "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", - 0.0f, - 1.0f); - RNA_def_boolean(ot->srna, - "use_front_faces_only", - false, - "Front Faces Only", - "Affect only faces facing towards the view"); + paint_mask_gesture_operator_properties(ot); } diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index 52cdebf3fd5..e709224f370 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -691,6 +691,14 @@ static float paint_space_stroke_spacing(bContext *C, spacing = spacing * (1.5f - spacing_pressure); } + if (SCULPT_is_cloth_deform_brush(brush)) { + /* The spacing in tools that use the cloth solver should not be affected by the brush radius to + * avoid affecting the simulation update rate when changing the radius of the brush. + With a value of 100 and the brush default of 10 for spacing, a simulation step runs every 2 + pixels movement of the cursor. */ + size_clamp = 100.0f; + } + /* stroke system is used for 2d paint too, so we need to account for * the fact that brush can be scaled there. */ spacing *= stroke->zoom_2d; @@ -1001,7 +1009,7 @@ bool paint_space_stroke_enabled(Brush *br, ePaintMode mode) return false; } - if (br->sculpt_tool == SCULPT_TOOL_CLOTH) { + if (br->sculpt_tool == SCULPT_TOOL_CLOTH || SCULPT_is_cloth_deform_brush(br)) { /* The Cloth Brush is a special case for stroke spacing. Even if it has grab modes which do * not support dynamic size, stroke spacing needs to be enabled so it is possible to control * whether the simulation runs constantly or only when the brush moves when using the cloth diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index e1c1b8ee5fb..7a066f35f23 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -2273,7 +2273,7 @@ static float brush_strength(const Sculpt *sd, case SCULPT_TOOL_DISPLACEMENT_ERASER: return alpha * pressure * overlap * feather; case SCULPT_TOOL_CLOTH: - if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { + if (ELEM(brush->cloth_deform_type, BRUSH_CLOTH_DEFORM_GRAB, BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) { /* Grab deform uses the same falloff as a regular grab brush. */ return root_alpha * feather; } @@ -6634,13 +6634,19 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo * generally used to create grab deformations. */ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) { - return ELEM(brush->sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_POSE, - SCULPT_TOOL_BOUNDARY, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_ELASTIC_DEFORM) || - SCULPT_is_cloth_deform_brush(brush); + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_POSE, + SCULPT_TOOL_BOUNDARY, + SCULPT_TOOL_THUMB, + SCULPT_TOOL_ELASTIC_DEFORM)) { + return true; + } + if (brush->sculpt_tool == SCULPT_TOOL_CLOTH && + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { + return true; + } + return false; } /* In these brushes the grab delta is calculated from the previous stroke location, which is used @@ -6648,7 +6654,7 @@ static bool sculpt_needs_delta_from_anchored_origin(Brush *brush) static bool sculpt_needs_delta_for_tip_orientation(Brush *brush) { if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { - return !SCULPT_is_cloth_deform_brush(brush); + return brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK; } return ELEM(brush->sculpt_tool, SCULPT_TOOL_CLAY_STRIPS, @@ -6694,7 +6700,9 @@ static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru copy_v3_v3(cache->orig_grab_location, cache->true_location); } } - else if (tool == SCULPT_TOOL_SNAKE_HOOK) { + else if (tool == SCULPT_TOOL_SNAKE_HOOK || + (tool == SCULPT_TOOL_CLOTH && + brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) { add_v3_v3(cache->true_location, cache->grab_delta); } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 9a3fbe474b8..c3666c8aaad 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -169,6 +169,8 @@ static void cloth_brush_add_length_constraint(SculptSession *ss, length_constraint->elem_position_a = cloth_sim->pos[v1]; length_constraint->elem_position_b = cloth_sim->pos[v2]; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_STRUCTURAL; + if (use_persistent) { length_constraint->length = len_v3v3(SCULPT_vertex_persistent_co_get(ss, v1), SCULPT_vertex_persistent_co_get(ss, v2)); @@ -201,6 +203,8 @@ static void cloth_brush_add_softbody_constraint(SculptClothSimulation *cloth_sim length_constraint->elem_position_a = cloth_sim->pos[v]; length_constraint->elem_position_b = cloth_sim->init_pos[v]; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_SOFTBODY; + length_constraint->length = 0.0f; length_constraint->strength = strength; @@ -220,6 +224,8 @@ static void cloth_brush_add_deformation_constraint(SculptClothSimulation *cloth_ length_constraint->elem_index_a = v; length_constraint->elem_index_b = v; + length_constraint->type = SCULPT_CLOTH_CONSTRAINT_DEFORMATION; + length_constraint->elem_position_a = cloth_sim->pos[v]; length_constraint->elem_position_b = cloth_sim->deformation_pos[v]; @@ -301,12 +307,18 @@ static void do_cloth_brush_build_constraints_task_cb_ex( if (brush && brush->sculpt_tool == SCULPT_TOOL_CLOTH) { /* The cloth brush works by applying forces in most of its modes, but some of them require * deformation coordinates to make the simulation stable. */ - if (cloth_is_deform_brush && len_squared < radius_squared) { - /* When a deform brush is used as part of the cloth brush, deformation constraints are - * created with different strengths and only inside the radius of the brush. */ + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB && len_squared < radius_squared) { + /* When the grab brush brush is used as part of the cloth brush, deformation constraints + * are created with different strengths and only inside the radius of the brush. */ const float fade = BKE_brush_curve_strength(brush, sqrtf(len_squared), ss->cache->radius); cloth_brush_add_deformation_constraint(data->cloth_sim, vd.index, fade); } + else if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) { + /* Cloth Snake Hook creates deformation constraint with fixed strength because the strength + * is controlled per iteration using cloth_sim->deformation_strength. */ + cloth_brush_add_deformation_constraint( + data->cloth_sim, vd.index, CLOTH_DEFORMATION_TARGET_STRENGTH); + } } else if (data->cloth_sim->deformation_pos) { /* Any other tool that target the cloth simulation handle the falloff in @@ -397,7 +409,7 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, brush, ss->cache->radius, ss->cache->initial_location, cloth_sim->init_pos[vd.index]); float current_vertex_location[3]; - if (SCULPT_is_cloth_deform_brush(brush)) { + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) { SCULPT_orig_vert_data_update(&orig_data, &vd); copy_v3_v3(current_vertex_location, orig_data.co); } @@ -456,6 +468,12 @@ static void do_cloth_brush_apply_forces_task_cb_ex(void *__restrict userdata, fade); zero_v3(force); break; + case BRUSH_CLOTH_DEFORM_SNAKE_HOOK: + copy_v3_v3(cloth_sim->deformation_pos[vd.index], cloth_sim->pos[vd.index]); + madd_v3_v3fl(cloth_sim->deformation_pos[vd.index], ss->cache->grab_delta_symmetry, fade); + cloth_sim->deformation_strength[vd.index] = fade; + zero_v3(force); + break; case BRUSH_CLOTH_DEFORM_PINCH_POINT: if (use_falloff_plane) { float distance = dist_signed_to_plane_v3(vd.co, deform_plane); @@ -718,13 +736,21 @@ static void cloth_brush_satisfy_constraints(SculptSession *ss, cloth_sim->init_pos[v2]) : 1.0f; + float deformation_strength = 1.0f; + if (constraint->type == SCULPT_CLOTH_CONSTRAINT_DEFORMATION) { + deformation_strength = (cloth_sim->deformation_strength[v1] + + cloth_sim->deformation_strength[v2]) * + 0.5f; + } + madd_v3_v3fl(cloth_sim->pos[v1], correction_vector_half, - 1.0f * mask_v1 * sim_factor_v1 * constraint->strength); + 1.0f * mask_v1 * sim_factor_v1 * constraint->strength * deformation_strength); if (v1 != v2) { madd_v3_v3fl(cloth_sim->pos[v2], correction_vector_half, - -1.0f * mask_v2 * sim_factor_v2 * constraint->strength); + -1.0f * mask_v2 * sim_factor_v2 * constraint->strength * + deformation_strength); } } } @@ -824,6 +850,15 @@ static void cloth_brush_apply_brush_foces(Sculpt *sd, Object *ob, PBVHNode **nod } } + if (brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_SNAKE_HOOK) { + /* Set the deformation strength to 0. Snake hook will initialize the strength in the required + * area. */ + const int totverts = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totverts; i++) { + ss->cache->cloth_sim->deformation_strength[i] = 0.0f; + } + } + TaskParallelSettings settings; BKE_pbvh_parallel_range_settings(&settings, true, totnode); BLI_task_parallel_range( @@ -862,6 +897,8 @@ SculptClothSimulation *SCULPT_cloth_brush_simulation_create(SculptSession *ss, if (brush && SCULPT_is_cloth_deform_brush(brush)) { cloth_sim->deformation_pos = MEM_calloc_arrayN( totverts, sizeof(float[3]), "cloth sim deformation positions"); + cloth_sim->deformation_strength = MEM_calloc_arrayN( + totverts, sizeof(float), "cloth sim deformation strength"); } cloth_sim->mass = cloth_mass; @@ -921,6 +958,7 @@ void SCULPT_cloth_brush_simulation_init(SculptSession *ss, SculptClothSimulation copy_v3_v3(cloth_sim->prev_pos[i], SCULPT_vertex_co_get(ss, i)); if (has_deformation_pos) { copy_v3_v3(cloth_sim->deformation_pos[i], SCULPT_vertex_co_get(ss, i)); + cloth_sim->deformation_strength[i] = 1.0f; } } } @@ -986,6 +1024,7 @@ void SCULPT_cloth_simulation_free(struct SculptClothSimulation *cloth_sim) MEM_SAFE_FREE(cloth_sim->length_constraint_tweak); MEM_SAFE_FREE(cloth_sim->deformation_pos); MEM_SAFE_FREE(cloth_sim->init_pos); + MEM_SAFE_FREE(cloth_sim->deformation_strength); if (cloth_sim->collider_list) { BKE_collider_cache_free(&cloth_sim->collider_list); } @@ -1071,6 +1110,25 @@ static EnumPropertyItem prop_cloth_filter_type[] = { {0, NULL, 0, NULL, NULL}, }; +static EnumPropertyItem prop_cloth_filter_orientation_items[] = { + {SCULPT_FILTER_ORIENTATION_LOCAL, + "LOCAL", + 0, + "Local", + "Use the local axis to limit the force and set the gravity direction"}, + {SCULPT_FILTER_ORIENTATION_WORLD, + "WORLD", + 0, + "World", + "Use the global axis to limit the force and set the gravity direction"}, + {SCULPT_FILTER_ORIENTATION_VIEW, + "VIEW", + 0, + "View", + "Use the view axis to limit the force and set the gravity direction"}, + {0, NULL, 0, NULL, NULL}, +}; + typedef enum eClothFilterForceAxis { CLOTH_FILTER_FORCE_X = 1 << 0, CLOTH_FILTER_FORCE_Y = 1 << 1, @@ -1120,7 +1178,15 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, switch (filter_type) { case CLOTH_FILTER_GRAVITY: - force[2] = -data->filter_strength * fade; + if (ss->filter_cache->orientation == SCULPT_FILTER_ORIENTATION_VIEW) { + /* When using the view orientation apply gravity in the -Y axis, this way objects will + * fall down instead of backwards. */ + force[1] = -data->filter_strength * fade; + } + else { + force[2] = -data->filter_strength * fade; + } + SCULPT_filter_to_object_space(force, ss->filter_cache); break; case CLOTH_FILTER_INFLATE: { float normal[3]; @@ -1138,11 +1204,13 @@ static void cloth_filter_apply_forces_task_cb(void *__restrict userdata, break; } + SCULPT_filter_to_orientation_space(force, ss->filter_cache); for (int axis = 0; axis < 3; axis++) { if (!ss->filter_cache->enabled_force_axis[axis]) { force[axis] = 0.0f; } } + SCULPT_filter_to_object_space(force, ss->filter_cache); add_v3_v3(force, sculpt_gravity); @@ -1264,6 +1332,9 @@ static int sculpt_cloth_filter_invoke(bContext *C, wmOperator *op, const wmEvent ss->filter_cache->enabled_force_axis[1] = force_axis & CLOTH_FILTER_FORCE_Y; ss->filter_cache->enabled_force_axis[2] = force_axis & CLOTH_FILTER_FORCE_Z; + SculptFilterOrientation orientation = RNA_enum_get(op->ptr, "orientation"); + ss->filter_cache->orientation = orientation; + WM_event_add_modal_handler(C, op); return OPERATOR_RUNNING_MODAL; } @@ -1297,6 +1368,12 @@ void SCULPT_OT_cloth_filter(struct wmOperatorType *ot) CLOTH_FILTER_FORCE_X | CLOTH_FILTER_FORCE_Y | CLOTH_FILTER_FORCE_Z, "Force axis", "Apply the force in the selected axis"); + RNA_def_enum(ot->srna, + "orientation", + prop_cloth_filter_orientation_items, + SCULPT_FILTER_ORIENTATION_LOCAL, + "Orientation", + "Orientation of the axis to limit the filter force"); RNA_def_float(ot->srna, "cloth_mass", 1.0f, diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 56e70a8d47a..619a1b975b6 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -181,7 +181,7 @@ void SCULPT_filter_cache_free(SculptSession *ss) MEM_SAFE_FREE(ss->filter_cache); } -typedef enum eSculptMeshFilterTypes { +typedef enum eSculptMeshFilterType { MESH_FILTER_SMOOTH = 0, MESH_FILTER_SCALE = 1, MESH_FILTER_INFLATE = 2, @@ -193,7 +193,7 @@ typedef enum eSculptMeshFilterTypes { MESH_FILTER_SHARPEN = 8, MESH_FILTER_ENHANCE_DETAILS = 9, MESH_FILTER_ERASE_DISPLACEMENT = 10, -} eSculptMeshFilterTypes; +} eSculptMeshFilterType; static EnumPropertyItem prop_mesh_filter_types[] = { {MESH_FILTER_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth mesh"}, @@ -258,7 +258,7 @@ static EnumPropertyItem prop_mesh_filter_orientation_items[] = { {0, NULL, 0, NULL, NULL}, }; -static bool sculpt_mesh_filter_needs_pmap(int filter_type, bool use_face_sets) +static bool sculpt_mesh_filter_needs_pmap(eSculptMeshFilterType filter_type, bool use_face_sets) { return use_face_sets || ELEM(filter_type, MESH_FILTER_SMOOTH, @@ -277,7 +277,7 @@ static void mesh_filter_task_cb(void *__restrict userdata, SculptSession *ss = data->ob->sculpt; PBVHNode *node = data->nodes[i]; - const int filter_type = data->filter_type; + const eSculptMeshFilterType filter_type = data->filter_type; SculptOrigVertData orig_data; SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[i]); @@ -486,49 +486,80 @@ static void mesh_filter_task_cb(void *__restrict userdata, static void mesh_filter_enhance_details_init_directions(SculptSession *ss) { const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->detail_directions = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "detail directions"); for (int i = 0; i < totvert; i++) { float avg[3]; SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(ss->filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); } } +static void mesh_filter_surface_smooth_init(SculptSession *ss, + const float shape_preservation, + const float current_vertex_displacement) +{ + const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->surface_smooth_laplacian_disp = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "surface smooth displacement"); + filter_cache->surface_smooth_shape_preservation = shape_preservation; + filter_cache->surface_smooth_current_vertex = current_vertex_displacement; +} + static void mesh_filter_init_limit_surface_co(SculptSession *ss) { const int totvert = SCULPT_vertex_count_get(ss); - ss->filter_cache->limit_surface_co = MEM_malloc_arrayN( - 3 * sizeof(float), totvert, "limit surface co"); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->limit_surface_co = MEM_malloc_arrayN( + sizeof(float[3]), totvert, "limit surface co"); for (int i = 0; i < totvert; i++) { - SCULPT_vertex_limit_surface_get(ss, i, ss->filter_cache->limit_surface_co[i]); + SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]); } } -static void mesh_filter_sharpen_init_factors(SculptSession *ss) +static void mesh_filter_sharpen_init(SculptSession *ss, + const float smooth_ratio, + const float intensify_detail_strength, + const int curvature_smooth_iterations) { const int totvert = SCULPT_vertex_count_get(ss); + FilterCache *filter_cache = ss->filter_cache; + + filter_cache->sharpen_smooth_ratio = smooth_ratio; + filter_cache->sharpen_intensify_detail_strength = intensify_detail_strength; + filter_cache->sharpen_curvature_smooth_iterations = curvature_smooth_iterations; + filter_cache->sharpen_factor = MEM_malloc_arrayN(sizeof(float), totvert, "sharpen factor"); + filter_cache->detail_directions = MEM_malloc_arrayN( + totvert, sizeof(float[3]), "sharpen detail direction"); + for (int i = 0; i < totvert; i++) { float avg[3]; SCULPT_neighbor_coords_average(ss, avg, i); - sub_v3_v3v3(ss->filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); - ss->filter_cache->sharpen_factor[i] = len_v3(ss->filter_cache->detail_directions[i]); + sub_v3_v3v3(filter_cache->detail_directions[i], avg, SCULPT_vertex_co_get(ss, i)); + filter_cache->sharpen_factor[i] = len_v3(filter_cache->detail_directions[i]); } float max_factor = 0.0f; for (int i = 0; i < totvert; i++) { - if (ss->filter_cache->sharpen_factor[i] > max_factor) { - max_factor = ss->filter_cache->sharpen_factor[i]; + if (filter_cache->sharpen_factor[i] > max_factor) { + max_factor = filter_cache->sharpen_factor[i]; } } max_factor = 1.0f / max_factor; for (int i = 0; i < totvert; i++) { - ss->filter_cache->sharpen_factor[i] *= max_factor; - ss->filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - ss->filter_cache->sharpen_factor[i]); + filter_cache->sharpen_factor[i] *= max_factor; + filter_cache->sharpen_factor[i] = 1.0f - pow2f(1.0f - filter_cache->sharpen_factor[i]); } /* Smooth the calculated factors and directions to remove high frecuency detail. */ for (int smooth_iterations = 0; - smooth_iterations < ss->filter_cache->sharpen_curvature_smooth_iterations; + smooth_iterations < filter_cache->sharpen_curvature_smooth_iterations; smooth_iterations++) { for (int i = 0; i < totvert; i++) { float direction_avg[3] = {0.0f, 0.0f, 0.0f}; @@ -537,15 +568,15 @@ static void mesh_filter_sharpen_init_factors(SculptSession *ss) SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { - add_v3_v3(direction_avg, ss->filter_cache->detail_directions[ni.index]); - sharpen_avg += ss->filter_cache->sharpen_factor[ni.index]; + add_v3_v3(direction_avg, filter_cache->detail_directions[ni.index]); + sharpen_avg += filter_cache->sharpen_factor[ni.index]; total++; } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); if (total > 0) { - mul_v3_v3fl(ss->filter_cache->detail_directions[i], direction_avg, 1.0f / total); - ss->filter_cache->sharpen_factor[i] = sharpen_avg / total; + mul_v3_v3fl(filter_cache->detail_directions[i], direction_avg, 1.0f / total); + filter_cache->sharpen_factor[i] = sharpen_avg / total; } } } @@ -590,7 +621,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int filter_type = RNA_enum_get(op->ptr, "type"); + eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type"); float filter_strength = RNA_float_get(op->ptr, "strength"); const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); @@ -654,17 +685,21 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent Object *ob = CTX_data_active_object(C); Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - int filter_type = RNA_enum_get(op->ptr, "type"); SculptSession *ss = ob->sculpt; - PBVH *pbvh = ob->sculpt->pbvh; - int deform_axis = RNA_enum_get(op->ptr, "deform_axis"); + const eMeshFilterDeformAxis deform_axis = RNA_enum_get(op->ptr, "deform_axis"); + const eSculptMeshFilterType filter_type = RNA_enum_get(op->ptr, "type"); + const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); + const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets); + if (deform_axis == 0) { + /* All axis are disabled, so the filter is not going to produce any deformation. */ return OPERATOR_CANCELLED; } - if (RNA_boolean_get(op->ptr, "use_face_sets")) { - /* Update the active vertex */ + if (use_face_sets) { + /* Update the active face set manually as the paint cursor is not enabled when using the Mesh + * Filter Tool. */ float mouse[2]; SculptCursorGeometryInfo sgi; mouse[0] = event->mval[0]; @@ -672,67 +707,48 @@ static int sculpt_mesh_filter_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); } - const bool use_face_sets = RNA_boolean_get(op->ptr, "use_face_sets"); - SCULPT_vertex_random_access_ensure(ss); - - const bool needs_topology_info = sculpt_mesh_filter_needs_pmap(filter_type, use_face_sets); BKE_sculpt_update_object_for_edit(depsgraph, ob, needs_topology_info, false, false); if (needs_topology_info) { SCULPT_boundary_info_ensure(ob); } - const int totvert = SCULPT_vertex_count_get(ss); - if (BKE_pbvh_type(pbvh) == PBVH_FACES && needs_topology_info && !ob->sculpt->pmap) { - return OPERATOR_CANCELLED; - } - - SCULPT_undo_push_begin("Mesh filter"); - - if (ELEM(filter_type, MESH_FILTER_RELAX, MESH_FILTER_RELAX_FACE_SETS)) { - SCULPT_boundary_info_ensure(ob); - } + SCULPT_undo_push_begin("Mesh Filter"); SCULPT_filter_cache_init(C, ob, sd, SCULPT_UNDO_COORDS); - if (use_face_sets) { - ss->filter_cache->active_face_set = SCULPT_active_face_set_get(ss); - } - else { - ss->filter_cache->active_face_set = SCULPT_FACE_SET_NONE; - } - - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SURFACE_SMOOTH) { - ss->filter_cache->surface_smooth_laplacian_disp = MEM_mallocN(sizeof(float[3]) * totvert, - "surface smooth disp"); - ss->filter_cache->surface_smooth_shape_preservation = RNA_float_get( - op->ptr, "surface_smooth_shape_preservation"); - ss->filter_cache->surface_smooth_current_vertex = RNA_float_get( - op->ptr, "surface_smooth_current_vertex"); - } - - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_SHARPEN) { - ss->filter_cache->sharpen_smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio"); - ss->filter_cache->sharpen_intensify_detail_strength = RNA_float_get( - op->ptr, "sharpen_intensify_detail_strength"); - ss->filter_cache->sharpen_curvature_smooth_iterations = RNA_int_get( - op->ptr, "sharpen_curvature_smooth_iterations"); - - ss->filter_cache->sharpen_factor = MEM_mallocN(sizeof(float) * totvert, "sharpen factor"); - ss->filter_cache->detail_directions = MEM_malloc_arrayN( - totvert, sizeof(float[3]), "sharpen detail direction"); - - mesh_filter_sharpen_init_factors(ss); - } - - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_ENHANCE_DETAILS) { - ss->filter_cache->detail_directions = MEM_malloc_arrayN( - totvert, sizeof(float[3]), "detail direction"); - mesh_filter_enhance_details_init_directions(ss); - } + FilterCache *filter_cache = ss->filter_cache; + filter_cache->active_face_set = use_face_sets ? SCULPT_active_face_set_get(ss) : + SCULPT_FACE_SET_NONE; - if (RNA_enum_get(op->ptr, "type") == MESH_FILTER_ERASE_DISPLACEMENT) { - mesh_filter_init_limit_surface_co(ss); + switch (filter_type) { + case MESH_FILTER_SURFACE_SMOOTH: { + const float shape_preservation = RNA_float_get(op->ptr, "surface_smooth_shape_preservation"); + const float current_vertex_displacement = RNA_float_get(op->ptr, + "surface_smooth_current_vertex"); + mesh_filter_surface_smooth_init(ss, shape_preservation, current_vertex_displacement); + break; + } + case MESH_FILTER_SHARPEN: { + const float smooth_ratio = RNA_float_get(op->ptr, "sharpen_smooth_ratio"); + const float intensify_detail_strength = RNA_float_get(op->ptr, + "sharpen_intensify_detail_strength"); + const int curvature_smooth_iterations = RNA_int_get(op->ptr, + "sharpen_curvature_smooth_iterations"); + mesh_filter_sharpen_init( + ss, smooth_ratio, intensify_detail_strength, curvature_smooth_iterations); + break; + } + case MESH_FILTER_ENHANCE_DETAILS: { + mesh_filter_enhance_details_init_directions(ss); + break; + } + case MESH_FILTER_ERASE_DISPLACEMENT: { + mesh_filter_init_limit_surface_co(ss); + break; + } + default: + break; } ss->filter_cache->enabled_axis[0] = deform_axis & MESH_FILTER_DEFORM_X; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 548ef00ad87..47a375a2318 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -398,8 +398,9 @@ void SCULPT_cloth_plane_falloff_preview_draw(const uint gpuattr, BLI_INLINE bool SCULPT_is_cloth_deform_brush(const Brush *brush) { - return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && - brush->cloth_deform_type == BRUSH_CLOTH_DEFORM_GRAB) || + return (brush->sculpt_tool == SCULPT_TOOL_CLOTH && ELEM(brush->cloth_deform_type, + BRUSH_CLOTH_DEFORM_GRAB, + BRUSH_CLOTH_DEFORM_SNAKE_HOOK)) || /* All brushes that are not the cloth brush deform the simulation using softbody constriants instead of applying forces. */ (brush->sculpt_tool != SCULPT_TOOL_CLOTH && diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 63fe8643628..87ee7480c92 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -66,25 +66,40 @@ void SCULPT_neighbor_coords_average_interior(SculptSession *ss, float result[3], { float avg[3] = {0.0f, 0.0f, 0.0f}; int total = 0; - - if (SCULPT_vertex_is_boundary(ss, index)) { - copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); - return; - } + int neighbor_count = 0; + const bool is_boundary = SCULPT_vertex_is_boundary(ss, index); SculptVertexNeighborIter ni; SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, index, ni) { - add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); - total++; + 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)); + total++; + } + } + else { + /* Interior vertices use all neighbors. */ + add_v3_v3(avg, SCULPT_vertex_co_get(ss, ni.index)); + total++; + } } SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); - if (total > 0) { - mul_v3_v3fl(result, avg, 1.0f / total); + /* Do not modify corner vertices. */ + if (neighbor_count <= 2) { + copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + return; } - else { + + /* Avoid division by 0 when there are no neighbors. */ + if (total == 0) { copy_v3_v3(result, SCULPT_vertex_co_get(ss, index)); + return; } + + mul_v3_v3fl(result, avg, 1.0f / total); } /* For bmesh: Average surrounding verts based on an orthogonality measure. @@ -316,7 +331,7 @@ static void do_smooth_brush_task_cb_ex(void *__restrict userdata, } else { float avg[3], val[3]; - SCULPT_neighbor_coords_average(ss, avg, vd.index); + SCULPT_neighbor_coords_average_interior(ss, avg, vd.index); sub_v3_v3v3(val, avg, vd.co); madd_v3_v3v3fl(val, vd.co, val, fade); SCULPT_clip(sd, ss, vd.co, val); diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index bdada4d2565..b52b04eba3a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -326,6 +326,12 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) MEM_SAFE_FREE(nodes); } + /* Update the viewport navigation rotation origin. */ + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + copy_v3_v3(ups->average_stroke_accum, ss->pivot_pos); + ups->average_stroke_counter = 1; + ups->last_stroke_valid = true; + ED_region_tag_redraw(region); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index e8eddc014db..d4f5f066d48 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -257,10 +257,10 @@ static void sound_update_animation_flags(Scene *scene) } scene->id.tag |= LIB_TAG_DOIT; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { BKE_sequencer_recursive_apply(seq, sound_update_animation_flags_fn, scene); } - SEQ_END; + SEQ_ALL_END; fcu = id_data_find_fcurve(&scene->id, scene, &RNA_Scene, "audio_volume", 0, &driven); if (fcu || driven) { diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index db55eff8284..cd4197d1df8 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -191,7 +191,6 @@ static void action_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); @@ -278,7 +277,6 @@ static void action_channel_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 3976e18d70c..e567b3ca54c 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -795,8 +795,12 @@ const char *buttons_context_dir[] = { "line_style", "collection", "gpencil", +#ifdef WITH_HAIR_NODES "hair", +#endif +#ifdef WITH_PARTICLE_NODES "pointcloud", +#endif "volume", NULL, }; diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index dc34e56dc92..d7cf2e4d544 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -292,9 +292,7 @@ static void buttons_main_region_layout_properties(const bContext *C, break; } - const bool vertical = true; - ED_region_panels_layout_ex( - C, region, ®ion->type->paneltypes, contexts, sbuts->mainb, vertical, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, NULL); } static void buttons_main_region_layout(const bContext *C, ARegion *region) diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index d27b80efd40..18df8774e09 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -926,7 +926,6 @@ static void clip_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); /* data... */ movieclip_main_area_set_view2d(C, region); @@ -1054,7 +1053,6 @@ static void graph_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); @@ -1099,7 +1097,6 @@ static void dopesheet_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); @@ -1172,7 +1169,6 @@ static void clip_channels_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 3a0125356f7..4b554e0c5c0 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -215,7 +215,6 @@ static void console_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); /* worlks best with no view2d matrix set */ UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 51e6c99ff39..7039eba7db1 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -441,7 +441,9 @@ static void draw_background(FileLayout *layout, View2D *v2d) uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColorShade(TH_BACK, -7); + float col_alternating[4]; + UI_GetThemeColor4fv(TH_ROW_ALTERNATE, col_alternating); + immUniformThemeColorBlend(TH_BACK, TH_ROW_ALTERNATE, col_alternating[3]); /* alternating flat shade background */ for (i = 2; (i <= layout->rows + 1); i += 2) { diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index e9ffd4583d7..8c4b2a1b8a6 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -1722,7 +1722,11 @@ void FILE_OT_execute(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = file_exec_invoke; ot->exec = file_exec; - ot->poll = file_operator_poll; + /* Important since handler is on window level. + * + * Avoid using #file_operator_poll since this is also used for entering directories + * which is used even when the file manager doesn't have an operator. */ + ot->poll = ED_operator_file_active; /* properties */ prop = RNA_def_boolean(ot->srna, @@ -2293,7 +2297,7 @@ static void file_expand_directory(bContext *C) } #else { - get_default_root(sfile->params->dir); + BLI_windows_get_default_root_dir(sfile->params->dir); } /* change "C:" --> "C:\", [#28102] */ else if ((isalpha(sfile->params->dir[0]) && (sfile->params->dir[1] == ':')) && diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 67ea22a7ef5..0ade50814e0 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1127,7 +1127,7 @@ static void parent_dir_until_exists_or_default_root(char *dir) { if (!BLI_path_parent_dir_until_exists(dir)) { #ifdef WIN32 - get_default_root(dir); + BLI_windows_get_default_root_dir(dir); #else strcpy(dir, "/"); #endif diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index f520f91b89b..61d6d8bf678 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -449,7 +449,6 @@ static void file_main_region_draw(const bContext *C, ARegion *region) FileSelectParams *params = ED_fileselect_get_params(sfile); View2D *v2d = ®ion->v2d; - float col[3]; /* Needed, because filelist is not initialized on loading */ if (!sfile->files || filelist_empty(sfile->files)) { @@ -457,9 +456,7 @@ static void file_main_region_draw(const bContext *C, ARegion *region) } /* clear and setup matrix */ - UI_GetThemeColor3fv(TH_BACK, col); - GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); + UI_ThemeClearColor(TH_BACK); /* Allow dynamically sliders to be set, saves notifiers etc. */ diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 4e0f60544e6..a1e75e2b9b2 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -200,12 +200,9 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) Scene *scene = CTX_data_scene(C); bAnimContext ac; View2D *v2d = ®ion->v2d; - float col[3]; /* clear and setup matrix */ - UI_GetThemeColor3fv(TH_BACK, col); - GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); + UI_ThemeClearColor(TH_BACK); UI_view2d_view_ortho(v2d); @@ -358,12 +355,9 @@ static void graph_channel_region_draw(const bContext *C, ARegion *region) { bAnimContext ac; View2D *v2d = ®ion->v2d; - float col[3]; /* clear and setup matrix */ - UI_GetThemeColor3fv(TH_BACK, col); - GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); + UI_ThemeClearColor(TH_BACK); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index d58f5ede7d7..058436a46bf 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -523,7 +523,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, GPU_shader_uniform_vector( state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); - immDrawPixelsTex(&state, x1, y1, rectx, recty, GL_R16F, false, rectf, zoomx, zoomy, NULL); + immDrawPixelsTex(&state, x1, y1, rectx, recty, GPU_R16F, false, rectf, zoomx, zoomy, NULL); MEM_freeN(rectf); } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index a64d5505ebe..a806e3e25d1 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -650,7 +650,6 @@ static void image_main_region_draw(const bContext *C, ARegion *region) GPU_framebuffer_bind(framebuffer_default); GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); - GPU_clear(GPU_COLOR_BIT); GPU_framebuffer_bind(framebuffer_overlay); @@ -661,8 +660,7 @@ static void image_main_region_draw(const bContext *C, ARegion *region) UI_GetThemeColor3fv(TH_BACK, col); srgb_to_linearrgb_v3_v3(col, col); GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); image_user_refresh_scene(C, sima); @@ -836,9 +834,7 @@ static void image_buttons_region_layout(const bContext *C, ARegion *region) break; } - const bool vertical = true; - ED_region_panels_layout_ex( - C, region, ®ion->type->paneltypes, contexts_base, -1, vertical, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts_base, NULL); } static void image_buttons_region_draw(const bContext *C, ARegion *region) diff --git a/source/blender/editors/space_info/info_draw.c b/source/blender/editors/space_info/info_draw.c index 72533b88406..595da97c75a 100644 --- a/source/blender/editors/space_info/info_draw.c +++ b/source/blender/editors/space_info/info_draw.c @@ -147,7 +147,6 @@ static int report_textview_begin(TextViewContext *tvc) tvc->iter = reports->list.last; UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); tvc->iter_tmp = 0; if (tvc->iter && report_textview_skip__internal(tvc)) { diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index e97031736ca..301e88b0904 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -426,7 +426,7 @@ static bool format_stats(Main *bmain, if (wm->is_interface_locked) { return false; } - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); stats_update(depsgraph, view_layer); } diff --git a/source/blender/editors/space_info/space_info.c b/source/blender/editors/space_info/space_info.c index b9153ec0cbd..8d3f21aefeb 100644 --- a/source/blender/editors/space_info/space_info.c +++ b/source/blender/editors/space_info/space_info.c @@ -142,7 +142,6 @@ static void info_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); /* quick way to avoid drawing if not bug enough */ if (region->winy < 16) { diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index bc9bd0e18f2..dc8f616c5e6 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -1847,11 +1847,7 @@ static int nlaedit_sync_actlen_exec(bContext *C, wmOperator *op) continue; } - /* recalculate the length of the action */ - calc_action_range(strip->act, &strip->actstart, &strip->actend, 0); - - /* adjust the strip extents in response to this */ - BKE_nlastrip_recalculate_bounds(strip); + BKE_nlastrip_recalculate_bounds_sync_action(strip); ale->update |= ANIM_UPDATE_DEPS; } diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 7bbfe451eed..7a0cd35ece1 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -194,7 +194,6 @@ static void nla_channel_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); @@ -238,7 +237,6 @@ static void nla_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 814473b0e9a..56e53e79a8d 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -1343,8 +1343,6 @@ static void node_draw_basis(const bContext *C, } } - UI_ThemeClearColor(color_id); - UI_block_end(C, node->block); UI_block_draw(C, node->block); node->block = NULL; @@ -1737,8 +1735,8 @@ void drawnodespace(const bContext *C, ARegion *region) UI_view2d_view_ortho(v2d); UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); + GPU_scissor_test(true); /* XXX snode->cursor set in coordspace for placing new nodes, used for drawing noodles too */ UI_view2d_region_to_view(®ion->v2d, diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c index 4f15cec8c84..654bb94cc78 100644 --- a/source/blender/editors/space_node/node_templates.c +++ b/source/blender/editors/space_node/node_templates.c @@ -281,7 +281,9 @@ static void node_socket_add_replace(const bContext *C, /* also preserve mapping for texture nodes */ if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE && - node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE) { + node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE && + /* White noise texture node does not have NodeTexBase. */ + node_from->storage != NULL && node_prev->storage != NULL) { memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase)); } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 1705b9dd606..fbef3aa07d7 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -182,7 +182,7 @@ static void restrictbutton_bone_visibility_fn(bContext *C, void *poin, void *UNU { Bone *bone = (Bone *)poin; - if (CTX_wm_window(C)->eventstate->ctrl) { + if (CTX_wm_window(C)->eventstate->shift) { restrictbutton_recursive_bone(bone, BONE_HIDDEN_P, (bone->flag & BONE_HIDDEN_P) != 0); } } @@ -194,7 +194,7 @@ static void restrictbutton_bone_select_fn(bContext *C, void *UNUSED(poin), void bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->ctrl) { + if (CTX_wm_window(C)->eventstate->shift) { restrictbutton_recursive_bone(bone, BONE_UNSELECTABLE, (bone->flag & BONE_UNSELECTABLE) != 0); } @@ -209,7 +209,7 @@ static void restrictbutton_ebone_select_fn(bContext *C, void *UNUSED(poin), void ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->ctrl) { + if (CTX_wm_window(C)->eventstate->shift) { restrictbutton_recursive_ebone( C, ebone, BONE_UNSELECTABLE, (ebone->flag & BONE_UNSELECTABLE) != 0); } @@ -224,7 +224,7 @@ static void restrictbutton_ebone_visibility_fn(bContext *C, void *UNUSED(poin), ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); } - if (CTX_wm_window(C)->eventstate->ctrl) { + if (CTX_wm_window(C)->eventstate->shift) { restrictbutton_recursive_ebone(C, ebone, BONE_HIDDEN_A, (ebone->flag & BONE_HIDDEN_A) != 0); } @@ -1300,7 +1300,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, 0, -1, -1, - TIP_("Restrict visibility in the 3D View")); + TIP_("Restrict visibility in the 3D View\n" + "* Shift to set children")); UI_but_func_set(bt, restrictbutton_bone_visibility_fn, bone, NULL); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); @@ -1321,7 +1322,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, 0, 0, 0, - TIP_("Restrict selection in the 3D View")); + TIP_("Restrict selection in the 3D View\n" + "* Shift to set children")); UI_but_func_set(bt, restrictbutton_bone_select_fn, ob->data, bone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); @@ -1345,7 +1347,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, 0, 0, 0, - TIP_("Restrict visibility in the 3D View")); + TIP_("Restrict visibility in the 3D View\n" + "* Shift to set children")); UI_but_func_set(bt, restrictbutton_ebone_visibility_fn, NULL, ebone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); @@ -1366,7 +1369,8 @@ static void outliner_draw_restrictbuts(uiBlock *block, 0, 0, 0, - TIP_("Restrict selection in the 3D View")); + TIP_("Restrict selection in the 3D View\n" + "* Shift to set children")); UI_but_func_set(bt, restrictbutton_ebone_select_fn, NULL, ebone); UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK); UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); @@ -2188,7 +2192,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr); switch ((GpencilModifierType)md->type) { case eGpencilModifierType_Noise: - data.icon = ICON_RNDCURVE; + data.icon = ICON_MOD_NOISE; break; case eGpencilModifierType_Subdiv: data.icon = ICON_MOD_SUBSURF; @@ -2232,6 +2236,15 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case eGpencilModifierType_Armature: data.icon = ICON_MOD_ARMATURE; break; + case eGpencilModifierType_Multiply: + data.icon = ICON_GP_MULTIFRAME_EDITING; + break; + case eGpencilModifierType_Time: + data.icon = ICON_MOD_TIME; + break; + case eGpencilModifierType_Texture: + data.icon = ICON_TEXTURE; + break; /* Default */ default: @@ -2354,6 +2367,11 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) data.icon = ICON_OUTLINER_DATA_GP_LAYER; break; } + case TSE_GPENCIL_EFFECT_BASE: + case TSE_GPENCIL_EFFECT: + data.drag_id = tselem->id; + data.icon = ICON_SHADERFX; + break; default: data.icon = ICON_DOT; break; @@ -2743,6 +2761,23 @@ static void outliner_icon_background_colors(float icon_color[4], float icon_bord icon_border[3] = 0.2f; } +/* Draw a rounded rectangle behind icons of active elements. */ +static void outliner_draw_active_indicator(const float minx, + const float miny, + const float maxx, + const float maxy, + const float icon_color[4], + const float icon_border[4]) +{ + const float ufac = UI_UNIT_X / 20.0f; + const float radius = UI_UNIT_Y / 4.0f; + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_aa(true, minx, miny + ufac, maxx, maxy - ufac, radius, icon_color); + UI_draw_roundbox_aa(false, minx, miny + ufac, maxx, maxy - ufac, radius, icon_border); + GPU_blend(GPU_BLEND_ALPHA); /* Roundbox disables. */ +} + static void outliner_draw_iconrow_doit(uiBlock *block, TreeElement *te, const uiFontStyle *fstyle, @@ -2756,31 +2791,19 @@ static void outliner_draw_iconrow_doit(uiBlock *block, TreeStoreElem *tselem = TREESTORE(te); if (active != OL_DRAWSEL_NONE) { - float ufac = UI_UNIT_X / 20.0f; float icon_color[4], icon_border[4]; outliner_icon_background_colors(icon_color, icon_border); if (active == OL_DRAWSEL_ACTIVE) { UI_GetThemeColor4fv(TH_EDITED_OBJECT, icon_color); icon_border[3] = 0.3f; } - UI_draw_roundbox_corner_set(UI_CNR_ALL); - - UI_draw_roundbox_aa(true, - (float)*offsx, - (float)ys + ufac, - (float)*offsx + UI_UNIT_X, - (float)ys + UI_UNIT_Y - ufac, - (float)UI_UNIT_Y / 4.0f, - icon_color); - /* border around it */ - UI_draw_roundbox_aa(false, - (float)*offsx, - (float)ys + ufac, - (float)*offsx + UI_UNIT_X, - (float)ys + UI_UNIT_Y - ufac, - (float)UI_UNIT_Y / 4.0f, - icon_border); - GPU_blend(GPU_BLEND_ALPHA); /* Roundbox disables. */ + + outliner_draw_active_indicator((float)*offsx, + (float)ys, + (float)*offsx + UI_UNIT_X, + (float)ys + UI_UNIT_Y, + icon_color, + icon_border); } if (tselem->flag & TSE_HIGHLIGHTED) { @@ -3052,23 +3075,12 @@ static void outliner_draw_tree_element(bContext *C, /* active circle */ if (active != OL_DRAWSEL_NONE) { - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(true, - (float)startx + offsx + UI_UNIT_X, - (float)*starty + ufac, - (float)startx + offsx + 2.0f * UI_UNIT_X, - (float)*starty + UI_UNIT_Y - ufac, - UI_UNIT_Y / 4.0f, - icon_bgcolor); - /* border around it */ - UI_draw_roundbox_aa(false, - (float)startx + offsx + UI_UNIT_X, - (float)*starty + ufac, - (float)startx + offsx + 2.0f * UI_UNIT_X, - (float)*starty + UI_UNIT_Y - ufac, - UI_UNIT_Y / 4.0f, - icon_border); - GPU_blend(GPU_BLEND_ALPHA); /* roundbox disables it */ + outliner_draw_active_indicator((float)startx + offsx + UI_UNIT_X, + (float)*starty, + (float)startx + offsx + 2.0f * UI_UNIT_X, + (float)*starty + UI_UNIT_Y, + icon_bgcolor, + icon_border); te->flag |= TE_ACTIVE; /* For lookup in display hierarchies. */ } diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index cd2fcd8e2cf..ad7346a5651 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -150,9 +150,22 @@ void OUTLINER_OT_highlight_update(wmOperatorType *ot) * \{ */ /* Open or close a tree element, optionally toggling all children recursively */ -void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all) -{ +void outliner_item_openclose(SpaceOutliner *space_outliner, + TreeElement *te, + bool open, + bool toggle_all) +{ + /* Prevent opening leaf elements in the tree unless in the Data API display mode because in that + * mode subtrees are empty unless expanded. */ + if (space_outliner->outlinevis != SO_DATA_API && BLI_listbase_is_empty(&te->subtree)) { + return; + } + + /* Don't allow collapsing the scene collection. */ TreeStoreElem *tselem = TREESTORE(te); + if (tselem->type == TSE_VIEW_COLLECTION_BASE) { + return; + } if (open) { tselem->flag &= ~TSE_CLOSED; @@ -191,15 +204,9 @@ static int outliner_item_openclose_modal(bContext *C, wmOperator *op, const wmEv /* Only toggle openclose on the same level as the first clicked element */ if (te->xs == data->x_location) { - outliner_item_openclose(te, data->open, false); + outliner_item_openclose(space_outliner, te, data->open, false); - /* Avoid rebuild if possible. */ - if (outliner_element_needs_rebuild_on_open_change(TREESTORE(te))) { - ED_region_tag_redraw(region); - } - else { - ED_region_tag_redraw_no_rebuild(region); - } + outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); } } @@ -238,14 +245,8 @@ static int outliner_item_openclose_invoke(bContext *C, wmOperator *op, const wmE const bool open = (tselem->flag & TSE_CLOSED) || (toggle_all && (outliner_flag_is_any_test(&te->subtree, TSE_CLOSED, 1))); - outliner_item_openclose(te, open, toggle_all); - /* Avoid rebuild if possible. */ - if (outliner_element_needs_rebuild_on_open_change(TREESTORE(te))) { - ED_region_tag_redraw(region); - } - else { - ED_region_tag_redraw_no_rebuild(region); - } + outliner_item_openclose(space_outliner, te, open, toggle_all); + outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); /* Only toggle once for single click toggling */ if (event->type == LEFTMOUSE) { diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 33dbbb274c0..bd283777397 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -237,7 +237,7 @@ void outliner_build_tree(struct Main *mainvar, struct SpaceOutliner *space_outliner, struct ARegion *region); -bool outliner_element_needs_rebuild_on_open_change(const TreeStoreElem *tselem); +bool outliner_mode_requires_always_rebuild(const struct SpaceOutliner *space_outliner); typedef struct IDsSelectedData { struct ListBase selected_array; @@ -374,7 +374,10 @@ void item_object_mode_exit_fn(struct bContext *C, void outliner_set_coordinates(struct ARegion *region, struct SpaceOutliner *space_outliner); -void outliner_item_openclose(TreeElement *te, bool open, bool toggle_all); +void outliner_item_openclose(struct SpaceOutliner *space_outliner, + TreeElement *te, + bool open, + bool toggle_all); /* outliner_dragdrop.c */ void outliner_dropboxes(void); @@ -515,6 +518,8 @@ float outliner_restrict_columns_width(const struct SpaceOutliner *space_outliner TreeElement *outliner_find_element_with_flag(const ListBase *lb, short flag); bool outliner_is_element_visible(const TreeElement *te); void outliner_scroll_view(struct ARegion *region, int delta_y); +void outliner_tag_redraw_avoid_rebuild_on_open_change(const struct SpaceOutliner *space_outliner, + struct ARegion *region); /* outliner_sync.c ---------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 1ac1b46f0d1..266ea293d43 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -46,6 +46,7 @@ #include "BKE_main.h" #include "BKE_object.h" #include "BKE_paint.h" +#include "BKE_report.h" #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_workspace.h" @@ -193,12 +194,17 @@ static void do_outliner_item_posemode_toggle( } else { bool ok = false; - if (ob->mode & OB_MODE_POSE) { + + if (ID_IS_LINKED(ob)) { + BKE_report(CTX_wm_reports(C), RPT_WARNING, "Cannot pose libdata"); + } + else if (ob->mode & OB_MODE_POSE) { ok = ED_object_posemode_exit_ex(bmain, ob); } else { ok = ED_object_posemode_enter_ex(bmain, ob); } + if (ok) { ED_object_base_select(base, (ob->mode & OB_MODE_POSE) ? BA_SELECT : BA_DESELECT); @@ -1000,7 +1006,9 @@ static eOLDrawState tree_element_active_master_collection(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); LayerCollection *layer_collection = view_layer->layer_collections.first; BKE_layer_collection_activate(view_layer, layer_collection); - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work + * when only the active collection changes. */ + WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL); } return OL_DRAWSEL_NONE; @@ -1022,7 +1030,9 @@ static eOLDrawState tree_element_active_layer_collection(bContext *C, LayerCollection *layer_collection = te->directdata; ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, layer_collection); BKE_layer_collection_activate(view_layer, layer_collection); - WM_main_add_notifier(NC_SCENE | ND_LAYER, NULL); + /* A very precise notifier - ND_LAYER alone is quite vague, we want to avoid unnecessary work + * when only the active collection changes. */ + WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL); } return OL_DRAWSEL_NONE; @@ -1507,7 +1517,7 @@ static int outliner_box_select_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); - ED_region_tag_redraw(region); + ED_region_tag_redraw_no_rebuild(region); ED_outliner_select_sync_from_outliner(C, space_outliner); @@ -1630,6 +1640,40 @@ static TreeElement *outliner_find_next_element(SpaceOutliner *space_outliner, Tr return te; } +static TreeElement *outliner_walk_left(SpaceOutliner *space_outliner, + TreeElement *te, + bool toggle_all) +{ + TreeStoreElem *tselem = TREESTORE(te); + + if (TSELEM_OPEN(tselem, space_outliner)) { + outliner_item_openclose(space_outliner, te, false, toggle_all); + } + /* Only walk up a level if the element is closed and not toggling expand */ + else if (!toggle_all && te->parent) { + te = te->parent; + } + + return te; +} + +static TreeElement *outliner_walk_right(SpaceOutliner *space_outliner, + TreeElement *te, + bool toggle_all) +{ + TreeStoreElem *tselem = TREESTORE(te); + + /* 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 = te->subtree.first; + } + else { + outliner_item_openclose(space_outliner, te, true, toggle_all); + } + + return te; +} + static TreeElement *do_outliner_select_walk(SpaceOutliner *space_outliner, TreeElement *te, const int direction, @@ -1646,10 +1690,10 @@ static TreeElement *do_outliner_select_walk(SpaceOutliner *space_outliner, te = outliner_find_next_element(space_outliner, te); break; case UI_SELECT_WALK_LEFT: - outliner_item_openclose(te, false, toggle_all); + te = outliner_walk_left(space_outliner, te, toggle_all); break; case UI_SELECT_WALK_RIGHT: - outliner_item_openclose(te, true, toggle_all); + te = outliner_walk_right(space_outliner, te, toggle_all); break; } @@ -1729,7 +1773,7 @@ static int outliner_walk_select_invoke(bContext *C, wmOperator *op, const wmEven outliner_walk_scroll(region, active_te); ED_outliner_select_sync_from_outliner(C, space_outliner); - ED_region_tag_redraw(region); + outliner_tag_redraw_avoid_rebuild_on_open_change(space_outliner, region); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 6532ff189b5..2a13f9d6a66 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -792,10 +792,11 @@ static void id_override_library_create_fn(bContext *C, } else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) { BKE_lib_override_library_create_from_id(bmain, id_root, true); - } - BKE_main_id_clear_newpoins(bmain); - BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + /* Cleanup. */ + BKE_main_id_clear_newpoins(bmain); + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + } } } @@ -827,6 +828,68 @@ static void id_override_library_reset_fn(bContext *C, } } +static void id_override_library_resync_fn(bContext *C, + ReportList *UNUSED(reports), + Scene *scene, + TreeElement *te, + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *UNUSED(user_data)) +{ + BLI_assert(TSE_IS_REAL_ID(tselem)); + ID *id_root = tselem->id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { + Main *bmain = CTX_data_main(C); + + id_root->tag |= LIB_TAG_DOIT; + + /* Tag all linked parents in tree hierarchy to be also overridden. */ + while ((te = te->parent) != NULL) { + if (!TSE_IS_REAL_ID(te->store_elem)) { + continue; + } + if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) { + break; + } + te->store_elem->id->tag |= LIB_TAG_DOIT; + } + + BKE_lib_override_library_resync(bmain, scene, CTX_data_view_layer(C), id_root); + } +} + +static void id_override_library_delete_fn(bContext *C, + ReportList *UNUSED(reports), + Scene *UNUSED(scene), + TreeElement *te, + TreeStoreElem *UNUSED(tsep), + TreeStoreElem *tselem, + void *UNUSED(user_data)) +{ + BLI_assert(TSE_IS_REAL_ID(tselem)); + ID *id_root = tselem->id; + + if (ID_IS_OVERRIDE_LIBRARY_REAL(id_root)) { + Main *bmain = CTX_data_main(C); + + id_root->tag |= LIB_TAG_DOIT; + + /* Tag all linked parents in tree hierarchy to be also overridden. */ + while ((te = te->parent) != NULL) { + if (!TSE_IS_REAL_ID(te->store_elem)) { + continue; + } + if (!ID_IS_OVERRIDE_LIBRARY_REAL(te->store_elem->id)) { + break; + } + te->store_elem->id->tag |= LIB_TAG_DOIT; + } + + BKE_lib_override_library_delete(bmain, id_root); + } +} + static void id_fake_user_set_fn(bContext *UNUSED(C), ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -1567,6 +1630,7 @@ static int outliner_delete_exec(bContext *C, wmOperator *op) DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); ED_outliner_select_sync_from_object_tag(C); return OPERATOR_FINISHED; @@ -1607,6 +1671,8 @@ typedef enum eOutlinerIdOpTypes { OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET, OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, + OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY, OUTLINER_IDOP_SINGLE, OUTLINER_IDOP_DELETE, OUTLINER_IDOP_REMAP, @@ -1653,6 +1719,18 @@ static const EnumPropertyItem prop_id_op_types[] = { 0, "Reset Library Override Hierarchy", "Reset this local override to its linked values, as well as its hierarchy of dependencies"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY, + "OVERRIDE_LIBRARY_RESYNC_HIERARCHY", + 0, + "Resync Library Override Hierarchy", + "Rebuild this local override from its linked reference, as well as its hierarchy of " + "dependencies"}, + {OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY, + "OVERRIDE_LIBRARY_DELETE_HIERARCHY", + 0, + "Delete Library Override Hierarchy", + "Delete this local override (including its hierarchy of override dependencies) and relink " + "its usages to the linked data-blocks"}, {0, "", 0, NULL, NULL}, {OUTLINER_IDOP_COPY, "COPY", ICON_COPYDOWN, "Copy", ""}, {OUTLINER_IDOP_PASTE, "PASTE", ICON_PASTEDOWN, "Paste", ""}, @@ -1683,6 +1761,10 @@ static bool outliner_id_operation_item_poll(bContext *C, case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: return true; + case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: + return true; + case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: + return true; case OUTLINER_IDOP_SINGLE: if (!space_outliner || ELEM(space_outliner->outlinevis, SO_SCENES, SO_VIEW_LAYER)) { return true; @@ -1818,7 +1900,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE: { - /* make local */ outliner_do_libdata_operation(C, op->reports, scene, @@ -1830,7 +1911,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_CREATE_HIERARCHY: { - /* make local */ outliner_do_libdata_operation(C, op->reports, scene, @@ -1842,7 +1922,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET: { - /* make local */ outliner_do_libdata_operation(C, op->reports, scene, @@ -1854,7 +1933,6 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) break; } case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESET_HIERARCHY: { - /* make local */ outliner_do_libdata_operation(C, op->reports, scene, @@ -1865,6 +1943,28 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op) ED_undo_push(C, "Reset Overridden Data Hierarchy"); break; } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_RESYNC_HIERARCHY: { + outliner_do_libdata_operation(C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + id_override_library_resync_fn, + &(OutlinerLibOverrideData){.do_hierarchy = true}); + ED_undo_push(C, "Resync Overridden Data Hierarchy"); + break; + } + case OUTLINER_IDOP_OVERRIDE_LIBRARY_DELETE_HIERARCHY: { + outliner_do_libdata_operation(C, + op->reports, + scene, + space_outliner, + &space_outliner->tree, + id_override_library_delete_fn, + &(OutlinerLibOverrideData){.do_hierarchy = true}); + ED_undo_push(C, "Delete Overridden Data Hierarchy"); + break; + } case OUTLINER_IDOP_SINGLE: { /* make single user */ switch (idlevel) { diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 60058c82283..22a7019ab91 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -32,6 +32,7 @@ #include "DNA_camera_types.h" #include "DNA_collection_types.h" #include "DNA_constraint_types.h" +#include "DNA_gpencil_modifier_types.h" #include "DNA_gpencil_types.h" #include "DNA_hair_types.h" #include "DNA_key_types.h" @@ -46,6 +47,7 @@ #include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" +#include "DNA_shader_fx_types.h" #include "DNA_simulation_types.h" #include "DNA_speaker_types.h" #include "DNA_volume_types.h" @@ -244,14 +246,12 @@ static TreeElement *outliner_add_element(SpaceOutliner *space_outliner, /* -------------------------------------------------------- */ /** - * Check if an element type needs a full rebuild if the open/collapsed state changes. - * These element types don't add children if collapsed. - * - * This current check isn't great really. A per element-type flag would be preferable. + * Check if a display mode needs a full rebuild if the open/collapsed state changes. + * Element types in these modes don't actually add children if collapsed, so the rebuild is needed. */ -bool outliner_element_needs_rebuild_on_open_change(const TreeStoreElem *tselem) +bool outliner_mode_requires_always_rebuild(const SpaceOutliner *space_outliner) { - return ELEM(tselem->type, TSE_RNA_STRUCT, TSE_RNA_PROPERTY, TSE_KEYMAP); + return ELEM(space_outliner->outlinevis, SO_DATA_API); } /* special handling of hierarchical non-lib data */ @@ -552,6 +552,70 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, } } + /* Grease Pencil modifiers. */ + if (!BLI_listbase_is_empty(&ob->greasepencil_modifiers)) { + TreeElement *ten_mod = outliner_add_element( + space_outliner, &te->subtree, ob, te, TSE_MODIFIER_BASE, 0); + + ten_mod->name = IFACE_("Modifiers"); + int index; + LISTBASE_FOREACH_INDEX (GpencilModifierData *, md, &ob->greasepencil_modifiers, index) { + TreeElement *ten = outliner_add_element( + space_outliner, &ten_mod->subtree, ob, ten_mod, TSE_MODIFIER, index); + ten->name = md->name; + ten->directdata = md; + + if (md->type == eGpencilModifierType_Armature) { + outliner_add_element(space_outliner, + &ten->subtree, + ((ArmatureGpencilModifierData *)md)->object, + ten, + TSE_LINKED_OB, + 0); + } + else if (md->type == eGpencilModifierType_Hook) { + outliner_add_element(space_outliner, + &ten->subtree, + ((HookGpencilModifierData *)md)->object, + ten, + TSE_LINKED_OB, + 0); + } + else if (md->type == eGpencilModifierType_Lattice) { + outliner_add_element(space_outliner, + &ten->subtree, + ((LatticeGpencilModifierData *)md)->object, + ten, + TSE_LINKED_OB, + 0); + } + } + } + + /* Grease Pencil effects. */ + if (!BLI_listbase_is_empty(&ob->shader_fx)) { + TreeElement *ten_fx = outliner_add_element( + space_outliner, &te->subtree, ob, te, TSE_GPENCIL_EFFECT_BASE, 0); + + ten_fx->name = IFACE_("Effects"); + int index; + LISTBASE_FOREACH_INDEX (ShaderFxData *, fx, &ob->shader_fx, index) { + TreeElement *ten = outliner_add_element( + space_outliner, &ten_fx->subtree, ob, ten_fx, TSE_GPENCIL_EFFECT, index); + ten->name = fx->name; + ten->directdata = fx; + + if (fx->type == eShaderFxType_Swirl) { + outliner_add_element(space_outliner, + &ten->subtree, + ((SwirlShaderFxData *)fx)->object, + ten, + TSE_LINKED_OB, + 0); + } + } + } + /* vertex groups */ if (ob->defbase.first) { bDeformGroup *defgroup; diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 1da44b5e51e..25dc7bc271e 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -37,6 +37,7 @@ #include "ED_armature.h" #include "ED_outliner.h" +#include "ED_screen.h" #include "UI_interface.h" #include "UI_view2d.h" @@ -455,6 +456,23 @@ void outliner_scroll_view(ARegion *region, int delta_y) } } +/** + * The outliner should generally use #ED_region_tag_redraw_no_rebuild() to avoid unnecessary tree + * rebuilds. If elements are open or closed, we may still have to rebuild. + * Upon changing the open/closed state, call this to avoid rebuilds if possible. + */ +void outliner_tag_redraw_avoid_rebuild_on_open_change(const SpaceOutliner *space_outliner, + ARegion *region) +{ + /* Avoid rebuild if possible. */ + if (outliner_mode_requires_always_rebuild(space_outliner)) { + ED_region_tag_redraw(region); + } + else { + ED_region_tag_redraw_no_rebuild(region); + } +} + /* Get base of object under cursor. Used for eyedropper tool */ Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2]) { diff --git a/source/blender/editors/space_outliner/space_outliner.c b/source/blender/editors/space_outliner/space_outliner.c index b14afed81dd..6a63c3c65c3 100644 --- a/source/blender/editors/space_outliner/space_outliner.c +++ b/source/blender/editors/space_outliner/space_outliner.c @@ -87,7 +87,6 @@ static void outliner_main_region_draw(const bContext *C, ARegion *region) /* clear */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); draw_outliner(C); @@ -114,6 +113,8 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), switch (wmn->data) { case ND_OB_ACTIVE: case ND_OB_SELECT: + ED_region_tag_redraw_no_rebuild(region); + break; case ND_OB_VISIBLE: case ND_OB_RENDER: case ND_MODE: @@ -121,15 +122,23 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), case ND_FRAME: case ND_RENDER_OPTIONS: case ND_SEQUENCER: - case ND_LAYER: case ND_LAYER_CONTENT: case ND_WORLD: case ND_SCENEBROWSE: ED_region_tag_redraw(region); break; + case ND_LAYER: + /* Avoid rebuild if only the active collection changes */ + if ((wmn->subtype == NS_LAYER_COLLECTION) && (wmn->action == NA_ACTIVATED)) { + ED_region_tag_redraw_no_rebuild(region); + break; + } + + ED_region_tag_redraw(region); + break; } - if (wmn->action & NA_EDITED) { - ED_region_tag_redraw(region); + if (wmn->action == NA_EDITED) { + ED_region_tag_redraw_no_rebuild(region); } break; case NC_OBJECT: @@ -181,7 +190,7 @@ static void outliner_main_region_listener(wmWindow *UNUSED(win), case NC_MATERIAL: switch (wmn->data) { case ND_SHADING_LINKS: - ED_region_tag_redraw(region); + ED_region_tag_redraw_no_rebuild(region); break; } break; diff --git a/source/blender/editors/space_script/space_script.c b/source/blender/editors/space_script/space_script.c index 4d0c2b658c6..3c3f7dc1e8e 100644 --- a/source/blender/editors/space_script/space_script.c +++ b/source/blender/editors/space_script/space_script.c @@ -127,7 +127,6 @@ static void script_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index 8a6b97b3834..bd4503dbe54 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -89,11 +89,12 @@ #define SEQ_SCROLLER_TEXT_OFFSET 8 #define MUTE_ALPHA 120 -/* Note, Don't use SEQ_BEGIN/SEQ_END while drawing! +/* Note, Don't use SEQ_ALL_BEGIN/SEQ_ALL_END while drawing! * it messes up transform. */ -#undef SEQ_BEGIN -#undef SEQP_BEGIN -#undef SEQ_END +#undef SEQ_ALL_BEGIN +#undef SEQ_ALL_END +#undef SEQ_CURRENT_BEGIN +#undef SEQ_CURRENT_END static Sequence *special_seq_update = NULL; @@ -357,7 +358,7 @@ static void draw_seq_waveform(View2D *v2d, static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2) { - /* Don't use SEQ_BEGIN/SEQ_END here, + /* Don't use SEQ_ALL_BEGIN/SEQ_ALL_END here, * because it changes seq->depth, which is needed for transform. */ Sequence *seq; uchar col[4]; @@ -1528,11 +1529,7 @@ static void sequencer_stop_running_jobs(const bContext *C, Scene *scene) static void sequencer_preview_clear(void) { - float col[3]; - - UI_GetThemeColor3fv(TH_SEQ_PREVIEW, col); - GPU_clear_color(col[0], col[1], col[2], 1.0f); - GPU_clear(GPU_COLOR_BIT); + UI_ThemeClearColor(TH_SEQ_PREVIEW); } static void sequencer_preview_get_rect(rctf *preview, @@ -1772,7 +1769,7 @@ void sequencer_draw_preview(const bContext *C, GPUFrameBuffer *framebuffer_overlay = GPU_viewport_framebuffer_overlay_get(viewport); GPU_framebuffer_bind_no_srgb(framebuffer_overlay); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); if (sseq->render_size == SEQ_PROXY_RENDER_SIZE_NONE) { sequencer_preview_clear(); @@ -2265,7 +2262,6 @@ void draw_timeline_seq(const bContext *C, ARegion *region) else { GPU_clear_color(col[0], col[1], col[2], 0.0f); } - GPU_clear(GPU_COLOR_BIT); UI_view2d_view_ortho(v2d); /* Get timeline boundbox, needed for the scrollers. */ diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 78ca2832c55..f175c2a7419 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -58,6 +58,7 @@ #include "ED_sequencer.h" #include "UI_interface.h" +#include "UI_resources.h" #include "UI_view2d.h" #include "DEG_depsgraph.h" @@ -216,7 +217,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports) file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); bool selected = false; /* Check for no selected strips */ - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE, SEQ_TYPE_META) || (seq->flag & SELECT) == 0) { continue; @@ -239,7 +240,7 @@ static void seq_proxy_build_job(const bContext *C, ReportList *reports) BKE_reportf(reports, RPT_WARNING, "Overwrite is not checked for %s, skipping", seq->name); } } - SEQ_END; + SEQ_CURRENT_END; if (!selected) { BKE_reportf(reports, RPT_WARNING, "Select movie or image strips"); @@ -482,10 +483,10 @@ void ED_sequencer_deselect_all(Scene *scene) return; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { seq->flag &= ~SEQ_ALLSEL; } - SEQ_END; + SEQ_CURRENT_END; } void recurs_sel_seq(Sequence *seqm) @@ -1030,7 +1031,7 @@ static void set_filter_seq(Scene *scene) return; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->flag & SELECT) { if (seq->type == SEQ_TYPE_MOVIE) { seq->flag |= SEQ_FILTERY; @@ -1039,7 +1040,7 @@ static void set_filter_seq(Scene *scene) } } } - SEQ_END; + SEQ_CURRENT_END; } #endif @@ -1065,7 +1066,7 @@ static void UNUSED_FUNCTION(seq_remap_paths)(Scene *scene) return; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->flag & SELECT) { if (STREQLEN(seq->strip->dir, from, strlen(from))) { printf("found %s\n", seq->strip->dir); @@ -1080,7 +1081,7 @@ static void UNUSED_FUNCTION(seq_remap_paths)(Scene *scene) } } } - SEQ_END; + SEQ_CURRENT_END; } /** \} */ @@ -1308,7 +1309,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) } /* Test for effects and overlap. - * Don't use SEQP_BEGIN since that would be recursive. */ + * Don't use SEQ_CURRENT_BEGIN since that would be recursive. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { if (seq->flag & SELECT && !(seq->depth == 0 && seq->flag & SEQ_LOCK)) { seq->flag &= ~SEQ_OVERLAP; @@ -2294,25 +2295,25 @@ static int sequencer_split_exec(bContext *C, wmOperator *op) Sequence *seq; if (ignore_selection) { if (use_cursor_position) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->enddisp == split_frame && seq->machine == split_channel) { seq_selected = seq->flag & SEQ_ALLSEL; } } - SEQ_END; + SEQ_CURRENT_END; if (!seq_selected) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->startdisp == split_frame && seq->machine == split_channel) { seq->flag &= ~SEQ_ALLSEL; } } - SEQ_END; + SEQ_CURRENT_END; } } } else { if (split_side != SEQ_SIDE_BOTH) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (split_side == SEQ_SIDE_LEFT) { if (seq->startdisp >= split_frame) { seq->flag &= ~SEQ_ALLSEL; @@ -2324,15 +2325,15 @@ static int sequencer_split_exec(bContext *C, wmOperator *op) } } } - SEQ_END; + SEQ_CURRENT_END; } } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->seq1 || seq->seq2 || seq->seq3) { BKE_sequence_calc(scene, seq); } } - SEQ_END; + SEQ_CURRENT_END; BKE_sequencer_sort(scene); } @@ -2376,6 +2377,28 @@ static int sequencer_split_invoke(bContext *C, wmOperator *op, const wmEvent *ev return sequencer_split_exec(C, op); } +static void sequencer_split_ui(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + + PointerRNA ptr; + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + uiLayout *row = uiLayoutRow(layout, false); + uiItemR(row, &ptr, "type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + uiItemR(layout, &ptr, "frame", 0, NULL, ICON_NONE); + uiItemR(layout, &ptr, "side", 0, NULL, ICON_NONE); + + uiItemS(layout); + + uiItemR(layout, &ptr, "use_cursor_position", 0, NULL, ICON_NONE); + if (RNA_boolean_get(&ptr, "use_cursor_position")) { + uiItemR(layout, &ptr, "channel", 0, NULL, ICON_NONE); + } +} + void SEQUENCER_OT_split(struct wmOperatorType *ot) { /* Identifiers. */ @@ -2387,6 +2410,7 @@ void SEQUENCER_OT_split(struct wmOperatorType *ot) ot->invoke = sequencer_split_invoke; ot->exec = sequencer_split_exec; ot->poll = sequencer_edit_poll; + ot->ui = sequencer_split_ui; /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2521,12 +2545,12 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) BKE_sequencer_prefetch_stop(scene); - SEQP_BEGIN (scene->ed, seq) { + SEQ_CURRENT_BEGIN (scene->ed, seq) { if (seq->flag & SELECT) { BKE_sequencer_flag_for_removal(scene, ed->seqbasep, seq); } } - SEQ_END; + SEQ_CURRENT_END; BKE_sequencer_remove_flagged_sequences(scene, ed->seqbasep); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -2952,7 +2976,7 @@ static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) recurs_del_seq_flag(scene, ed->seqbasep, SEQ_FLAG_DELETE, 0); /* Test for effects and overlap - * don't use SEQP_BEGIN since that would be recursive. */ + * don't use SEQ_CURRENT_BEGIN since that would be recursive. */ for (seq = ed->seqbasep->first; seq; seq = seq->next) { if (seq->flag & SELECT) { seq->flag &= ~SEQ_OVERLAP; @@ -3476,7 +3500,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) file_list = BLI_gset_new(BLI_ghashutil_strhash_p, BLI_ghashutil_strcmp, "file list"); - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { ListBase queue = {NULL, NULL}; LinkData *link; @@ -3493,7 +3517,7 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) BKE_sequencer_free_imbuf(scene, &ed->seqbase, false); } } - SEQ_END; + SEQ_CURRENT_END; BLI_gset_free(file_list, MEM_freeN); @@ -3544,7 +3568,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) turnon = false; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE, SEQ_TYPE_META)) { BKE_sequencer_proxy_set(seq, turnon); @@ -3589,7 +3613,7 @@ static int sequencer_enable_proxies_exec(bContext *C, wmOperator *op) } } } - SEQ_END; + SEQ_CURRENT_END; WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -3966,12 +3990,12 @@ static int sequencer_export_subtitles_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - SEQ_BEGIN (ed, seq) { + SEQ_ALL_BEGIN (ed, seq) { if (seq->type == SEQ_TYPE_TEXT) { BLI_addtail(&text_seq, MEM_dupallocN(seq)); } } - SEQ_END; + SEQ_ALL_END; if (BLI_listbase_is_empty(&text_seq)) { BKE_report(op->reports, RPT_ERROR, "No subtitles (text strips) to export"); diff --git a/source/blender/editors/space_sequencer/sequencer_modifier.c b/source/blender/editors/space_sequencer/sequencer_modifier.c index e0f7179c3f9..50f2a5084ef 100644 --- a/source/blender/editors/space_sequencer/sequencer_modifier.c +++ b/source/blender/editors/space_sequencer/sequencer_modifier.c @@ -232,7 +232,7 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - SEQP_BEGIN (ed, seq_iter) { + SEQ_CURRENT_BEGIN (ed, seq_iter) { if (seq_iter->flag & SELECT) { if (seq_iter == seq) { continue; @@ -254,7 +254,7 @@ static int strip_modifier_copy_exec(bContext *C, wmOperator *op) BKE_sequence_modifier_list_copy(seq_iter, seq); } } - SEQ_END; + SEQ_CURRENT_END; BKE_sequence_invalidate_cache_preprocessed(scene, seq); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 85b70354ab3..955b4dba5e8 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -415,14 +415,14 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) const float x = UI_view2d_region_to_view_x(v2d, mval[0]); - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (((x < CFRA) && (seq->enddisp <= CFRA)) || ((x >= CFRA) && (seq->startdisp >= CFRA))) { /* Select left or right. */ seq->flag |= SELECT; recurs_sel_seq(seq); } } - SEQ_END; + SEQ_CURRENT_END; { SpaceSeq *sseq = CTX_wm_space_seq(C); @@ -975,7 +975,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) ED_sequencer_deselect_all(scene); } const int cfra = CFRA; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { bool test = false; switch (side) { case -1: @@ -994,7 +994,7 @@ static int sequencer_select_side_of_frame_exec(bContext *C, wmOperator *op) recurs_sel_seq(seq); } } - SEQ_END; + SEQ_CURRENT_END; ED_outliner_select_sync_from_sequence_tag(C); @@ -1282,13 +1282,13 @@ static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel Sequence *seq; bool changed = false; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1299,13 +1299,13 @@ static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int c bool changed = false; const bool is_sound = SEQ_IS_SOUND(actseq); - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1316,14 +1316,14 @@ static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int bool changed = false; const bool is_effect = SEQ_IS_EFFECT(actseq); - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && (is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1339,45 +1339,45 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } if (SEQ_HAS_PATH(actseq) && dir) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip && STREQ(seq->strip->dir, dir)) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; } else if (actseq->type == SEQ_TYPE_SCENE) { Scene *sce = actseq->scene; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; } else if (actseq->type == SEQ_TYPE_MOVIECLIP) { MovieClip *clip = actseq->clip; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP && seq->clip == clip) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; } else if (actseq->type == SEQ_TYPE_MASK) { struct Mask *mask = actseq->mask; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; } return changed; @@ -1394,15 +1394,15 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann effects[i] = false; } - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) && ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) { effects[seq->type] = true; } } - SEQ_END; + SEQ_CURRENT_END; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) { if (seq->seq1) { seq->seq1->flag |= SELECT; @@ -1416,7 +1416,7 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1426,13 +1426,13 @@ static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq) Sequence *seq; bool changed = false; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) { seq->flag |= SELECT; changed = true; } } - SEQ_END; + SEQ_CURRENT_END; return changed; } @@ -1447,10 +1447,10 @@ static bool select_grouped_effect_link(Editing *ed, Sequence *actseq, const int int machine = actseq->machine; SeqIterator iter; - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { seq->tmp = NULL; } - SEQ_END; + SEQ_CURRENT_END; actseq->tmp = POINTER_FROM_INT(true); @@ -1523,11 +1523,11 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) bool changed = false; if (!extend) { - SEQP_BEGIN (ed, seq) { + SEQ_CURRENT_BEGIN (ed, seq) { seq->flag &= ~SELECT; changed = true; } - SEQ_END; + SEQ_CURRENT_END; } switch (type) { diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index f6d00ec94bf..300f63761c0 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -296,7 +296,6 @@ static void text_main_region_draw(const bContext *C, ARegion *region) /* clear and setup matrix */ UI_ThemeClearColor(TH_BACK); - GPU_clear(GPU_COLOR_BIT); // UI_view2d_view_ortho(v2d); diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index f1b1d6760f3..ec5175eae51 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1660,25 +1660,16 @@ void draw_text_main(SpaceText *st, ARegion *region) } if (st->showlinenrs && !wrap_skip) { - /* draw line number */ - if (tmp == text->curl) { - UI_FontThemeColor(tdc.font_id, TH_HILITE); - } - else { - UI_FontThemeColor(tdc.font_id, TH_LINENUMBERS); - } - + /* Draw line number. */ + UI_FontThemeColor(tdc.font_id, (tmp == text->curl) ? TH_HILITE : TH_LINENUMBERS); BLI_snprintf(linenr, sizeof(linenr), "%*d", st->runtime.line_number_display_digits, i + linecount + 1); - /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */ text_font_draw(&tdc, TXT_NUMCOL_PAD * st->runtime.cwidth_px, y, linenr); - - if (tmp == text->curl) { - UI_FontThemeColor(tdc.font_id, TH_TEXT); - } + /* Change back to text color. */ + UI_FontThemeColor(tdc.font_id, TH_TEXT); } if (st->wordwrap) { diff --git a/source/blender/editors/space_userpref/space_userpref.c b/source/blender/editors/space_userpref/space_userpref.c index 0242bb4fe24..3efdee9cec9 100644 --- a/source/blender/editors/space_userpref/space_userpref.c +++ b/source/blender/editors/space_userpref/space_userpref.c @@ -141,8 +141,7 @@ static void userpref_main_region_layout(const bContext *C, ARegion *region) BLI_str_tolower_ascii(id_lower, strlen(id_lower)); } - ED_region_panels_layout_ex( - C, region, ®ion->type->paneltypes, contexts, U.space_data.section_active, true, NULL); + ED_region_panels_layout_ex(C, region, ®ion->type->paneltypes, contexts, NULL); } static void userpref_operatortypes(void) diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index e5ba27cef07..de0b420a3b5 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -1323,9 +1323,7 @@ void ED_view3d_buttons_region_layout_ex(const bContext *C, paneltypes = &art->paneltypes; } - const bool vertical = true; - ED_region_panels_layout_ex( - C, region, paneltypes, contexts_base, -1, vertical, category_override); + ED_region_panels_layout_ex(C, region, paneltypes, contexts_base, category_override); } static void view3d_buttons_region_layout(const bContext *C, ARegion *region) @@ -1453,7 +1451,7 @@ static void view3d_tools_region_init(wmWindowManager *wm, ARegion *region) static void view3d_tools_region_draw(const bContext *C, ARegion *region) { - ED_region_panels_ex(C, region, (const char *[]){CTX_data_mode_string(C), NULL}, -1, true); + ED_region_panels_ex(C, region, (const char *[]){CTX_data_mode_string(C), NULL}); } /* area (not region) level listener */ diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 33b365b45aa..195199c45cd 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1618,7 +1618,7 @@ void view3d_main_region_draw(const bContext *C, ARegion *region) GPU_pass_cache_garbage_collect(); /* No depth test for drawing action zones afterwards. */ - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); v3d->flag |= V3D_INVALID_BACKBUF; } @@ -2319,14 +2319,14 @@ void ED_view3d_draw_depth_gpencil(Depsgraph *depsgraph, Scene *scene, ARegion *r /* Setup view matrix. */ ED_view3d_draw_setup_view(NULL, NULL, depsgraph, scene, region, v3d, NULL, NULL, NULL); - GPU_clear(GPU_DEPTH_BIT); + GPU_clear_depth(1.0f); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); GPUViewport *viewport = WM_draw_region_get_viewport(region); DRW_draw_depth_loop_gpencil(depsgraph, region, v3d, viewport); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } /* *********************** customdata **************** */ diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 6c61c83731d..6c2f4df7004 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -586,23 +586,23 @@ static void draw_primitive_view(const struct bContext *C, ARegion *UNUSED(region UI_GetThemeColor3fv(TH_GIZMO_PRIMARY, color); const bool use_depth = !XRAY_ENABLED(ipd->v3d); - const bool depth_test_enabled = GPU_depth_test_enabled(); + const eGPUDepthTest depth_test_enabled = GPU_depth_test_get(); if (use_depth) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); color[3] = 0.15f; draw_primitive_view_impl(C, ipd, color); } if (use_depth) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } color[3] = 1.0f; draw_primitive_view_impl(C, ipd, color); if (use_depth) { if (depth_test_enabled == false) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } } } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 9490c807989..f99301371d4 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -970,7 +970,7 @@ static bool do_lasso_select_curve(ViewContext *vc, /* Deselect items that were not added to selection (indicated by temp flag). */ if (deselect_all) { - BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); + data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); } if (data.is_changed) { @@ -2772,7 +2772,7 @@ static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel /* Deselect items that were not added to selection (indicated by temp flag). */ if (deselect_all) { - BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); + data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); } BKE_curve_nurb_vert_active_validate(vc->obedit->data); @@ -3693,7 +3693,6 @@ static bool nurbscurve_circle_select(ViewContext *vc, const bool select = (sel_op != SEL_OP_SUB); const bool deselect_all = (sel_op == SEL_OP_SET); CircleSelectUserData data; - bool changed = false; view3d_userdata_circleselect_init(&data, vc, select, mval, rad); @@ -3711,12 +3710,12 @@ static bool nurbscurve_circle_select(ViewContext *vc, /* Deselect items that were not added to selection (indicated by temp flag). */ if (deselect_all) { - BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); + data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT); } BKE_curve_nurb_vert_active_validate(vc->obedit->data); - return changed || data.is_changed; + return data.is_changed; } static void latticecurve_circle_doSelect(void *userData, BPoint *bp, const float screen_co[2]) diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index b986ebb75b6..d015b5dcc89 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1100,7 +1100,7 @@ int view3d_opengl_select(ViewContext *vc, wm, vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, &rect); if (!XRAY_ACTIVE(v3d)) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } /* If in xray mode, we select the wires in priority. */ @@ -1165,7 +1165,7 @@ int view3d_opengl_select(ViewContext *vc, wm, vc->win, depsgraph, scene, region, v3d, vc->rv3d->viewmat, NULL, NULL); if (!XRAY_ACTIVE(v3d)) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } DRW_opengl_context_disable(); diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 0aa6b4f6131..4e5eaf4bf51 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -785,7 +785,6 @@ void drawConstraint(TransInfo *t) else { if (tc->mode & CON_SELECT) { float vec[3]; - int depth_test_enabled; convertViewVec(t, vec, (t->mval[0] - t->con.imval[0]), (t->mval[1] - t->con.imval[1])); add_v3_v3(vec, t->center_global); @@ -794,9 +793,9 @@ void drawConstraint(TransInfo *t) drawLine(t, t->center_global, t->spacemtx[1], 'Y', 0); drawLine(t, t->center_global, t->spacemtx[2], 'Z', 0); - depth_test_enabled = GPU_depth_test_enabled(); + eGPUDepthTest depth_test_enabled = GPU_depth_test_get(); if (depth_test_enabled) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } const uint shdr_pos = GPU_vertformat_attr_add( @@ -821,7 +820,7 @@ void drawConstraint(TransInfo *t) immUnbindProgram(); if (depth_test_enabled) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } } @@ -843,7 +842,6 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) if (t->flag & T_PROP_EDIT) { RegionView3D *rv3d = CTX_wm_region_view3d(C); float tmat[4][4], imat[4][4]; - int depth_test_enabled; if (t->spacetype == SPACE_VIEW3D && rv3d != NULL) { copy_m4_m4(tmat, rv3d->viewmat); @@ -873,9 +871,9 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) GPU_matrix_scale_2f(1.0f, (ysize / xsize) * (xmask / ymask)); } - depth_test_enabled = GPU_depth_test_enabled(); + eGPUDepthTest depth_test_enabled = GPU_depth_test_get(); if (depth_test_enabled) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -899,7 +897,7 @@ void drawPropCircle(const struct bContext *C, TransInfo *t) immUnbindProgram(); if (depth_test_enabled) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } GPU_matrix_pop(); diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index 61af4ebbe46..4bf0f842f2f 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -346,7 +346,7 @@ static void set_trans_object_base_flags(TransInfo *t) ViewLayer *view_layer = t->view_layer; View3D *v3d = t->view; Scene *scene = t->scene; - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); /* NOTE: if Base selected and has parent selected: * base->flag_legacy = BA_WAS_SEL */ @@ -421,7 +421,7 @@ static int count_proportional_objects(TransInfo *t) View3D *v3d = t->view; struct Main *bmain = CTX_data_main(t->context); Scene *scene = t->scene; - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); /* Clear all flags we need. It will be used to detect dependencies. */ trans_object_base_deps_flag_prepare(view_layer); /* Rotations around local centers are allowed to propagate, so we take all objects. */ diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index dffee72205b..14ef5e87534 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -1343,7 +1343,7 @@ void drawDial3d(const TransInfo *t) BLI_assert(axis_idx >= MAN_AXIS_RANGE_ROT_START && axis_idx < MAN_AXIS_RANGE_ROT_END); gizmo_get_axis_color(axis_idx, NULL, color, color); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_blend(GPU_BLEND_ALPHA); GPU_line_smooth(true); @@ -1359,7 +1359,7 @@ void drawDial3d(const TransInfo *t) }); GPU_line_smooth(false); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); GPU_blend(GPU_BLEND_NONE); } } diff --git a/source/blender/editors/transform/transform_mode_edge_slide.c b/source/blender/editors/transform/transform_mode_edge_slide.c index 45debe964f4..fe97a9fba87 100644 --- a/source/blender/editors/transform/transform_mode_edge_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_slide.c @@ -1147,7 +1147,7 @@ void drawEdgeSlide(TransInfo *t) const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f; - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_blend(GPU_BLEND_ALPHA); @@ -1266,7 +1266,7 @@ void drawEdgeSlide(TransInfo *t) GPU_blend(GPU_BLEND_NONE); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } static void edge_slide_snap_apply(TransInfo *t, float *value) diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c index 11d0b375e6f..4367dd5ee92 100644 --- a/source/blender/editors/transform/transform_mode_vert_slide.c +++ b/source/blender/editors/transform/transform_mode_vert_slide.c @@ -390,7 +390,7 @@ void drawVertSlide(TransInfo *t) const int alpha_shade = -160; int i; - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_blend(GPU_BLEND_ALPHA); @@ -485,7 +485,7 @@ void drawVertSlide(TransInfo *t) GPU_matrix_pop(); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } } } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 09b5df82c2b..5db41570e00 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -184,7 +184,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) const float *loc_prev = NULL; const float *normal = NULL; - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); RegionView3D *rv3d = CTX_wm_region_view3d(C); if (!BLI_listbase_is_empty(&t->tsnap.points)) { @@ -228,7 +228,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) ED_gizmotypes_snap_3d_draw_util( rv3d, loc_prev, loc_cur, normal, col, activeCol, t->tsnap.snapElem); - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } } else if (t->spacetype == SPACE_IMAGE) { diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index faeefcb989e..044fca2310c 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -451,13 +451,13 @@ static void draw_uvs(SpaceImage *sima, GPU_batch_program_set_builtin(batch->edges, shader); /* Inner Line. Use depth test to insure selection is drawn on top. */ - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); GPU_line_width(1.0f); GPU_batch_uniform_4fv(batch->edges, "edgeColor", col1); GPU_batch_uniform_4fv(batch->edges, "selectColor", col2); GPU_batch_uniform_1f(batch->edges, "dashWidth", dash_width); GPU_batch_draw(batch->edges); - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); GPU_provoking_vertex(GPU_VERTEX_LAST); } @@ -486,7 +486,12 @@ static void draw_uvs(SpaceImage *sima, GPU_batch_uniform_4fv(batch->verts, "pinnedColor", pinned_col); GPU_batch_uniform_1f(batch->verts, "pointSize", (point_size + 1.5f) * M_SQRT2); GPU_batch_uniform_1f(batch->verts, "outlineWidth", 0.75f); - GPU_batch_draw(batch->verts); + + /* #GPU_batch_draw_advanced is needed as unbinding the shader and redrawing + * causes the vertices not to draw at the right size. */ + GPU_shader_bind(batch->verts->shader); + + GPU_batch_draw_advanced(batch->verts, 0, 0, 0, 0); /* We have problem in this mode when face order make some verts * appear unselected because an adjacent face is not selected and @@ -495,7 +500,11 @@ static void draw_uvs(SpaceImage *sima, * on top. A bit overkill but it's simple. */ GPU_batch_uniform_4fv(batch->verts, "vertColor", transparent); GPU_batch_uniform_4fv(batch->verts, "selectColor", col2); - GPU_batch_draw(batch->verts); + + GPU_batch_draw_advanced(batch->verts, 0, 0, 0, 0); + + GPU_shader_unbind(); + /* Finish #GPU_batch_draw_advanced drawing. */ GPU_blend(GPU_BLEND_NONE); GPU_program_point_size(false); @@ -552,8 +561,8 @@ void ED_uvedit_draw_main(SpaceImage *sima, Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, ((View3D *)NULL), &objects_len); if (objects_len > 0) { + GPU_depth_mask(true); GPU_clear_depth(1.0f); - GPU_clear(GPU_DEPTH_BIT); } /* go over all objects and create the batches + add their areas to the total */ diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 5a510aaf945..306f8a2c561 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -42,9 +42,6 @@ typedef struct UvNearestHit { /** Always set if we have a hit. */ struct BMFace *efa; struct BMLoop *l; - struct MLoopUV *luv, *luv_next; - /** Index of loop within face. */ - int lindex; /** Needs to be set before calling nearest functions. */ float dist_sq; } UvNearestHit; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 149c5cf1f96..2ea78ca5377 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -71,10 +71,14 @@ #include "uvedit_intern.h" static void uv_select_all_perform(Scene *scene, Object *obedit, int action); + +static void uv_select_all_perform_multi_ex( + Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude); static void uv_select_all_perform_multi(Scene *scene, Object **objects, const uint objects_len, int action); + static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object *obedit, @@ -612,7 +616,7 @@ void uvedit_uv_select_disable(const Scene *scene, } } -static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(Scene *scene, +static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(const Scene *scene, BMLoop *l_src, const int cd_loop_uv_offset) { @@ -637,6 +641,37 @@ static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(Scene *scene return l_other; } +static BMLoop *uvedit_loop_find_other_boundary_loop_with_visible_face(const Scene *scene, + BMLoop *l_edge, + BMVert *v_pivot, + const int cd_loop_uv_offset) +{ + BLI_assert(uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_edge, cd_loop_uv_offset) == NULL); + + BMLoop *l_step = l_edge; + l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next; + BMLoop *l_step_last = NULL; + do { + BLI_assert(BM_vert_in_edge(l_step->e, v_pivot)); + l_step_last = l_step; + l_step = uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step, cd_loop_uv_offset); + if (l_step) { + l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next; + } + } while (l_step != NULL); + + BM_elem_flag_set(l_step_last->e, BM_ELEM_SMOOTH, false); + + if (l_step_last != NULL) { + BLI_assert(uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step_last, cd_loop_uv_offset) == NULL); + } + + return l_step_last; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -671,9 +706,6 @@ bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNea hit->efa = efa; hit->l = l; - hit->luv = luv; - hit->luv_next = luv_next; - hit->lindex = i; hit->dist_sq = dist_test_sq; found = true; @@ -713,7 +745,6 @@ bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNea if (uv_find_nearest_edge(scene, obedit, co, &hit)) { hit.dist_sq = dist_sq_init; hit.l = NULL; - hit.luv = hit.luv_next = NULL; BMIter iter; BMFace *efa; @@ -783,7 +814,6 @@ bool uv_find_nearest_vert(Scene *scene, hit.dist_sq = dist_sq_init; hit.l = NULL; - hit.luv = hit.luv_next = NULL; BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; @@ -822,10 +852,7 @@ bool uv_find_nearest_vert(Scene *scene, hit.dist_sq = dist_test_sq; hit.l = l; - hit.luv = luv; - hit.luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); hit.efa = efa; - hit.lindex = i; found = true; } } @@ -979,200 +1006,235 @@ BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene, /** \name Edge Loop Select * \{ */ -static void uv_select_edgeloop_vertex_loop_flag(UvMapVert *first) -{ - UvMapVert *iterv; - int count = 0; +/** Mode for selecting edge loops at boundaries. */ +enum eUVEdgeLoopBoundaryMode { + /** Delimit at face corners (don't walk over multiple edges in the same face). */ + UV_EDGE_LOOP_BOUNDARY_LOOP = 1, + /** Don't delimit, walk over the all connected boundary loops. */ + UV_EDGE_LOOP_BOUNDARY_ALL = 2, +}; - for (iterv = first; iterv; iterv = iterv->next) { - if (iterv->separate && iterv != first) { - break; +static BMLoop *bm_select_edgeloop_double_side_next(const Scene *scene, + BMLoop *l_step, + BMVert *v_from, + const int cd_loop_uv_offset) +{ + if (l_step->f->len == 4) { + BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from); + BMLoop *l_step_over = (v_from == l_step->v) ? l_step->next : l_step->prev; + l_step_over = uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step_over, cd_loop_uv_offset); + if (l_step_over) { + return (l_step_over->v == v_from_next) ? l_step_over->prev : l_step_over->next; } - - count++; - } - - if (count < 5) { - first->flag = 1; } + return NULL; } -static UvMapVert *uv_select_edgeloop_vertex_map_get(UvVertMap *vmap, BMFace *efa, BMLoop *l) +static BMLoop *bm_select_edgeloop_single_side_next(const Scene *scene, + BMLoop *l_step, + BMVert *v_from, + const int cd_loop_uv_offset) { - UvMapVert *iterv, *first; - first = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v)); + BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from); + return uvedit_loop_find_other_boundary_loop_with_visible_face( + scene, l_step, v_from_next, cd_loop_uv_offset); +} - for (iterv = first; iterv; iterv = iterv->next) { - if (iterv->separate) { - first = iterv; - } - if (iterv->poly_index == BM_elem_index_get(efa)) { - return first; +/* TODO(campbell): support this in the BMesh API, as we have for clearing other types. */ +static void bm_loop_tags_clear(BMesh *bm) +{ + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l_iter, BM_ELEM_TAG); } } - - return NULL; } -static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, - UvMapVert *first1, - UvMapVert *first2, - int *totface) +/** + * Tag all loops which should be selected, the caller must select. + */ +static void uv_select_edgeloop_double_side_tag(const Scene *scene, + BMEditMesh *em, + BMLoop *l_init_pair[2], + const int cd_loop_uv_offset) { - UvMapVert *iterv1, *iterv2; - BMFace *efa; - int tot = 0; - - /* count number of faces this edge has */ - for (iterv1 = first1; iterv1; iterv1 = iterv1->next) { - if (iterv1->separate && iterv1 != first1) { - break; - } + bm_loop_tags_clear(em->bm); - for (iterv2 = first2; iterv2; iterv2 = iterv2->next) { - if (iterv2->separate && iterv2 != first2) { + for (int side = 0; side < 2; side++) { + BMLoop *l_step_pair[2] = {l_init_pair[0], l_init_pair[1]}; + BMVert *v_from = side ? l_step_pair[0]->e->v1 : l_step_pair[0]->e->v2; + /* Disable since we start from the same edge. */ + BM_elem_flag_disable(l_step_pair[0], BM_ELEM_TAG); + BM_elem_flag_disable(l_step_pair[1], BM_ELEM_TAG); + while ((l_step_pair[0] != NULL) && (l_step_pair[1] != NULL)) { + if (!uvedit_face_visible_test(scene, l_step_pair[0]->f) || + !uvedit_face_visible_test(scene, l_step_pair[1]->f) || + /* Check loops have not diverged. */ + (uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step_pair[0], cd_loop_uv_offset) != l_step_pair[1])) { break; } - if (iterv1->poly_index == iterv2->poly_index) { - /* if face already tagged, don't do this edge */ - efa = BM_face_at_index(em->bm, iterv1->poly_index); - if (BM_elem_flag_test(efa, BM_ELEM_TAG)) { - return false; - } + BLI_assert(l_step_pair[0]->e == l_step_pair[1]->e); + + BM_elem_flag_enable(l_step_pair[0], BM_ELEM_TAG); + BM_elem_flag_enable(l_step_pair[1], BM_ELEM_TAG); + + BMVert *v_from_next = BM_edge_other_vert(l_step_pair[0]->e, v_from); + /* Walk over both sides, ensure they keep on the same edge. */ + for (int i = 0; i < ARRAY_SIZE(l_step_pair); i++) { + l_step_pair[i] = bm_select_edgeloop_double_side_next( + scene, l_step_pair[i], v_from, cd_loop_uv_offset); + } - tot++; + if ((l_step_pair[0] && BM_elem_flag_test(l_step_pair[0], BM_ELEM_TAG)) || + (l_step_pair[1] && BM_elem_flag_test(l_step_pair[1], BM_ELEM_TAG))) { break; } + v_from = v_from_next; } } +} - if (*totface == 0) { /* start edge */ - *totface = tot; - } - else if (tot != *totface) { /* check for same number of faces as start edge */ - return false; +/** + * Tag all loops which should be selected, the caller must select. + * + * \param r_count_by_select: Count the number of unselected and selected loops, + * this is needed to implement cycling between #eUVEdgeLoopBoundaryMode. + */ +static void uv_select_edgeloop_single_side_tag(const Scene *scene, + BMEditMesh *em, + BMLoop *l_init, + const int cd_loop_uv_offset, + enum eUVEdgeLoopBoundaryMode boundary_mode, + int r_count_by_select[2]) +{ + if (r_count_by_select) { + r_count_by_select[0] = r_count_by_select[1] = 0; } - /* tag the faces */ - for (iterv1 = first1; iterv1; iterv1 = iterv1->next) { - if (iterv1->separate && iterv1 != first1) { - break; - } + bm_loop_tags_clear(em->bm); - for (iterv2 = first2; iterv2; iterv2 = iterv2->next) { - if (iterv2->separate && iterv2 != first2) { + for (int side = 0; side < 2; side++) { + BMLoop *l_step = l_init; + BMVert *v_from = side ? l_step->e->v1 : l_step->e->v2; + /* Disable since we start from the same edge. */ + BM_elem_flag_disable(l_step, BM_ELEM_TAG); + while (l_step != NULL) { + + if (!uvedit_face_visible_test(scene, l_step->f) || + /* Check the boundary is still a boundary. */ + (uvedit_loop_find_other_radial_loop_with_visible_face( + scene, l_step, cd_loop_uv_offset) != NULL)) { break; } - if (iterv1->poly_index == iterv2->poly_index) { - efa = BM_face_at_index(em->bm, iterv1->poly_index); - BM_elem_flag_enable(efa, BM_ELEM_TAG); + if (r_count_by_select != NULL) { + r_count_by_select[uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)] += 1; + /* Early exit when mixed could be optional if needed. */ + if (r_count_by_select[0] && r_count_by_select[1]) { + r_count_by_select[0] = r_count_by_select[1] = -1; + break; + } + } + + BM_elem_flag_enable(l_step, BM_ELEM_TAG); + + BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from); + BMFace *f_step_prev = l_step->f; + + l_step = bm_select_edgeloop_single_side_next(scene, l_step, v_from, cd_loop_uv_offset); + + if (l_step && BM_elem_flag_test(l_step, BM_ELEM_TAG)) { break; } + if (boundary_mode == UV_EDGE_LOOP_BOUNDARY_LOOP) { + /* Don't allow walking over the the face. */ + if (f_step_prev == l_step->f) { + break; + } + } + v_from = v_from_next; } } - - return true; } -static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend) +static int uv_select_edgeloop( + SpaceImage *sima, Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend) { BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMFace *efa; - BMIter iter, liter; - BMLoop *l; - UvVertMap *vmap; - UvMapVert *iterv_curr; - UvMapVert *iterv_next; - int starttotf; - bool looking, select; + bool select; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* setup */ - BM_mesh_elem_table_ensure(em->bm, BM_FACE); - vmap = BM_uv_vert_map_create(em->bm, false, false); - - BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); - - if (!extend) { - uv_select_all_perform(scene, obedit, SEL_DESELECT); + if (extend) { + select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset)); + } + else { + select = true; } - BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); - - /* set flags for first face and verts */ - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l); - iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next); - uv_select_edgeloop_vertex_loop_flag(iterv_curr); - uv_select_edgeloop_vertex_loop_flag(iterv_next); - - starttotf = 0; - uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf); - - /* sorry, first edge isn't even ok */ - looking = !(iterv_curr->flag == 0 && iterv_next->flag == 0); - - /* iterate */ - while (looking) { - looking = false; - - /* find correct valence edges which are not tagged yet, but connect to tagged one */ + BMLoop *l_init_pair[2] = { + hit->l, + uvedit_loop_find_other_radial_loop_with_visible_face(scene, hit->l, cd_loop_uv_offset), + }; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!BM_elem_flag_test(efa, BM_ELEM_TAG) && uvedit_face_visible_test(scene, efa)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - /* check face not hidden and not tagged */ - if (!(iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l))) { - continue; - } - if (!(iterv_next = uv_select_edgeloop_vertex_map_get(vmap, efa, l->next))) { - continue; - } + /* When selecting boundaries, support cycling between selection modes. */ + enum eUVEdgeLoopBoundaryMode boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP; - /* check if vertex is tagged and has right valence */ - if (iterv_curr->flag || iterv_next->flag) { - if (uv_select_edgeloop_edge_tag_faces(em, iterv_curr, iterv_next, &starttotf)) { - looking = true; - BM_elem_flag_enable(efa, BM_ELEM_TAG); + /* Tag all loops that are part of the edge loop (select after). + * This is done so we can */ + if (l_init_pair[1] == NULL) { + int count_by_select[2]; + /* If the loops selected toggle the boundaries. */ + uv_select_edgeloop_single_side_tag( + scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, count_by_select); + if (count_by_select[!select] == 0) { + boundary_mode = UV_EDGE_LOOP_BOUNDARY_ALL; - uv_select_edgeloop_vertex_loop_flag(iterv_curr); - uv_select_edgeloop_vertex_loop_flag(iterv_next); - break; - } - } - } + /* If the boundary is selected, toggle back to the loop. */ + uv_select_edgeloop_single_side_tag( + scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, count_by_select); + if (count_by_select[!select] == 0) { + boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP; } } } - /* do the actual select/deselect */ - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l); - iterv_next = uv_select_edgeloop_vertex_map_get(vmap, hit->efa, hit->l->next); - iterv_curr->flag = 1; - iterv_next->flag = 1; - - if (extend) { - select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset)); + if (l_init_pair[1] == NULL) { + uv_select_edgeloop_single_side_tag( + scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, NULL); } else { - select = true; + uv_select_edgeloop_double_side_tag(scene, em, l_init_pair, cd_loop_uv_offset); } - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - iterv_curr = uv_select_edgeloop_vertex_map_get(vmap, efa, l); + /* Apply the selection. */ + if (!extend) { + uv_select_all_perform(scene, obedit, SEL_DESELECT); + } - if (iterv_curr->flag) { - uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset); + /* Select all tagged loops. */ + { + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { + uvedit_edge_select_set_with_sticky( + sima, scene, em, l_iter, select, false, cd_loop_uv_offset); + } } } } - /* cleanup */ - BM_uv_vert_map_free(vmap); - return (select) ? 1 : -1; } @@ -1770,10 +1832,8 @@ static void uv_select_all_perform(Scene *scene, Object *obedit, int action) } } -static void uv_select_all_perform_multi(Scene *scene, - Object **objects, - const uint objects_len, - int action) +static void uv_select_all_perform_multi_ex( + Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude) { if (action == SEL_TOGGLE) { action = uvedit_select_is_any_selected_multi(scene, objects, objects_len) ? SEL_DESELECT : @@ -1782,10 +1842,21 @@ static void uv_select_all_perform_multi(Scene *scene, for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; + if (ob_exclude && (obedit == ob_exclude)) { + continue; + } uv_select_all_perform(scene, obedit, action); } } +static void uv_select_all_perform_multi(Scene *scene, + Object **objects, + const uint objects_len, + int action) +{ + uv_select_all_perform_multi_ex(scene, objects, objects_len, action, NULL); +} + static int uv_select_all_exec(bContext *C, wmOperator *op) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); @@ -1931,8 +2002,7 @@ static int uv_mouse_select_multi(bContext *C, /* do selection */ if (selectmode == UV_SELECT_ISLAND) { if (!extend) { - /* TODO(MULTI_EDIT): We only need to de-select non-active */ - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit); } /* Current behavior of 'extend' * is actually toggling, so pass extend flag as 'toggle' here */ @@ -2122,12 +2192,11 @@ static int uv_mouse_select_loop_generic_multi(bContext *C, /* Do selection. */ if (!extend) { - /* TODO(MULTI_EDIT): We only need to de-select non-active */ - uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); + uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit); } if (loop_type == UV_LOOP_SELECT) { - flush = uv_select_edgeloop(scene, obedit, &hit, extend); + flush = uv_select_edgeloop(sima, scene, obedit, &hit, extend); } else if (loop_type == UV_RING_SELECT) { flush = uv_select_edgering(sima, scene, obedit, &hit, extend); diff --git a/source/blender/functions/tests/FN_array_spans_test.cc b/source/blender/functions/tests/FN_array_spans_test.cc index 9a632b58be8..af2bc0aad91 100644 --- a/source/blender/functions/tests/FN_array_spans_test.cc +++ b/source/blender/functions/tests/FN_array_spans_test.cc @@ -50,7 +50,9 @@ TEST(virtual_array_span, MultipleArrayConstructor) std::array<int, 2> values1 = {6, 7}; std::array<int, 1> values2 = {8}; std::array<const int *, 3> starts = {values0.data(), values1.data(), values2.data()}; - std::array<int64_t, 3> sizes{values0.size(), values1.size(), values2.size()}; + std::array<int64_t, 3> sizes{static_cast<int64_t>(values0.size()), + static_cast<int64_t>(values1.size()), + static_cast<int64_t>(values2.size())}; VArraySpan<int> span{starts, sizes}; EXPECT_EQ(span.size(), 3); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index 45b379c5e0a..b319a9d91a3 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -84,7 +84,7 @@ set(SRC intern/gpu_shader_interface.cc intern/gpu_state.cc intern/gpu_texture.cc - intern/gpu_uniformbuffer.cc + intern/gpu_uniform_buffer.cc intern/gpu_vertex_buffer.cc intern/gpu_vertex_format.cc intern/gpu_viewport.c @@ -93,7 +93,9 @@ set(SRC opengl/gl_context.cc opengl/gl_drawlist.cc opengl/gl_shader.cc + opengl/gl_shader_interface.cc opengl/gl_state.cc + opengl/gl_uniform_buffer.cc opengl/gl_vertex_array.cc GPU_attr_binding.h @@ -119,10 +121,9 @@ set(SRC GPU_primitive.h GPU_select.h GPU_shader.h - GPU_shader_interface.h GPU_state.h GPU_texture.h - GPU_uniformbuffer.h + GPU_uniform_buffer.h GPU_vertex_buffer.h GPU_vertex_format.h GPU_viewport.h @@ -140,7 +141,9 @@ set(SRC intern/gpu_private.h intern/gpu_select_private.h intern/gpu_shader_private.hh + intern/gpu_shader_interface.hh intern/gpu_state_private.hh + intern/gpu_uniform_buffer_private.hh intern/gpu_vertex_format_private.h opengl/gl_backend.hh @@ -148,7 +151,9 @@ set(SRC opengl/gl_context.hh opengl/gl_drawlist.hh opengl/gl_shader.hh + opengl/gl_shader_interface.hh opengl/gl_state.hh + opengl/gl_uniform_buffer.hh opengl/gl_vertex_array.hh ) diff --git a/source/blender/gpu/GPU_context.h b/source/blender/gpu/GPU_context.h index e3d47cfe084..be7e604fb96 100644 --- a/source/blender/gpu/GPU_context.h +++ b/source/blender/gpu/GPU_context.h @@ -27,7 +27,6 @@ #include "GPU_batch.h" #include "GPU_common.h" -#include "GPU_shader_interface.h" #ifdef __cplusplus extern "C" { diff --git a/source/blender/gpu/GPU_extensions.h b/source/blender/gpu/GPU_extensions.h index 2ce6e458378..18ac2265cc4 100644 --- a/source/blender/gpu/GPU_extensions.h +++ b/source/blender/gpu/GPU_extensions.h @@ -40,7 +40,6 @@ int GPU_max_color_texture_samples(void); int GPU_max_cube_map_size(void); int GPU_max_ubo_binds(void); int GPU_max_ubo_size(void); -float GPU_max_line_width(void); void GPU_get_dfdy_factors(float fac[2]); bool GPU_arb_base_instance_is_supported(void); bool GPU_arb_texture_cube_map_array_is_supported(void); diff --git a/source/blender/gpu/GPU_framebuffer.h b/source/blender/gpu/GPU_framebuffer.h index 7103317e4d6..db25bbb7998 100644 --- a/source/blender/gpu/GPU_framebuffer.h +++ b/source/blender/gpu/GPU_framebuffer.h @@ -225,7 +225,6 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs, void GPU_clear_color(float red, float green, float blue, float alpha); void GPU_clear_depth(float depth); -void GPU_clear(eGPUFrameBufferBits flags); void GPU_frontbuffer_read_pixels( int x, int y, int w, int h, int channels, eGPUDataFormat format, void *data); diff --git a/source/blender/gpu/GPU_immediate.h b/source/blender/gpu/GPU_immediate.h index 41d4f5d28d3..6057770d2d9 100644 --- a/source/blender/gpu/GPU_immediate.h +++ b/source/blender/gpu/GPU_immediate.h @@ -29,7 +29,6 @@ #include "GPU_immediate_util.h" #include "GPU_primitive.h" #include "GPU_shader.h" -#include "GPU_shader_interface.h" #include "GPU_texture.h" #include "GPU_vertex_format.h" @@ -103,13 +102,11 @@ void immVertex2iv(uint attr_id, const int data[2]); /* Provide uniform values that don't change for the entire draw call. */ void immUniform1i(const char *name, int x); -void immUniform4iv(const char *name, const int data[4]); void immUniform1f(const char *name, float x); void immUniform2f(const char *name, float x, float y); void immUniform2fv(const char *name, const float data[2]); void immUniform3f(const char *name, float x, float y, float z); void immUniform3fv(const char *name, const float data[3]); -void immUniformArray3fv(const char *name, const float *data, int count); void immUniform4f(const char *name, float x, float y, float z, float w); void immUniform4fv(const char *name, const float data[4]); void immUniformArray4fv(const char *bare_name, const float *data, int count); diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 9dcf9b7d5bb..680e717e615 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -39,7 +39,7 @@ struct GPUNode; struct GPUNodeLink; struct GPUNodeStack; struct GPUTexture; -struct GPUUniformBuffer; +struct GPUUniformBuf; struct Image; struct ImageUser; struct ListBase; @@ -158,10 +158,10 @@ bool GPU_stack_link(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out, ...); -GPUNodeLink *GPU_uniformbuffer_link_out(struct GPUMaterial *mat, - struct bNode *node, - struct GPUNodeStack *stack, - const int index); +GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, + struct bNode *node, + struct GPUNodeStack *stack, + const int index); void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); @@ -169,9 +169,9 @@ void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3], const short *falloff_type, const float *sharpness); -struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, - int sample_len, - struct GPUTexture **tex_profile); +struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, + int sample_len, + struct GPUTexture **tex_profile); /* High level functions to create and use GPU materials */ GPUMaterial *GPU_material_from_nodetree_find(struct ListBase *gpumaterials, @@ -201,9 +201,9 @@ struct GPUShader *GPU_material_get_shader(GPUMaterial *material); struct Material *GPU_material_get_material(GPUMaterial *material); eGPUMaterialStatus GPU_material_status(GPUMaterial *mat); -struct GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material); +struct GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material); void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs); -struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void); +struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void); bool GPU_material_has_surface_output(GPUMaterial *mat); bool GPU_material_has_volume_output(GPUMaterial *mat); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 99fcae19984..33fef266c42 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -27,19 +27,12 @@ extern "C" { #endif -struct GPUShaderInterface; struct GPUTexture; -struct GPUUniformBuffer; +struct GPUUniformBuf; struct GPUVertBuf; -/* TODO(fclem) These members should be private and the - * whole struct should just be an opaque pointer. */ -typedef struct GPUShader { - /** Uniform & attribute locations for shader. */ - struct GPUShaderInterface *interface; - /** For debugging purpose. */ - char name[64]; -} GPUShader; +/** Opaque type hidding blender::gpu::Shader */ +typedef struct GPUShader GPUShader; typedef enum eGPUShaderTFBType { GPU_SHADER_TFB_NONE = 0, /* Transform feedback unsupported. */ @@ -90,6 +83,41 @@ void GPU_shader_transform_feedback_disable(GPUShader *shader); int GPU_shader_get_program(GPUShader *shader); +typedef enum { + GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */ + GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */ + GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */ + GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */ + GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */ + GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */ + + GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */ + GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */ + GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */ + GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */ + GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */ + + GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */ + GPU_UNIFORM_ORCO, /* vec4 OrcoTexCoFactors[] */ + GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */ + + GPU_UNIFORM_COLOR, /* vec4 color */ + GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */ + GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */ + GPU_UNIFORM_RESOURCE_ID, /* int resourceId */ + GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */ + + GPU_NUM_UNIFORMS, /* Special value, denotes number of builtin uniforms. */ +} GPUUniformBuiltin; + +typedef enum { + GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */ + GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */ + GPU_UNIFORM_BLOCK_INFO, /* infoBlock */ + + GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ +} GPUUniformBlockBuiltin; + void GPU_shader_set_srgb_uniform(GPUShader *shader); int GPU_shader_get_uniform(GPUShader *shader, const char *name); @@ -123,8 +151,6 @@ void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, cons int GPU_shader_get_attribute(GPUShader *shader, const char *name); -char *GPU_shader_get_binary(GPUShader *shader, uint *r_binary_format, int *r_binary_len); - void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear); /* Builtin/Non-generated shaders */ diff --git a/source/blender/gpu/GPU_shader_interface.h b/source/blender/gpu/GPU_shader_interface.h deleted file mode 100644 index 47e4e432d66..00000000000 --- a/source/blender/gpu/GPU_shader_interface.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 by Mike Erwin. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - * - * GPU shader interface (C --> GLSL) - */ - -#pragma once - -#include "GPU_common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - GPU_UNIFORM_MODEL = 0, /* mat4 ModelMatrix */ - GPU_UNIFORM_VIEW, /* mat4 ViewMatrix */ - GPU_UNIFORM_MODELVIEW, /* mat4 ModelViewMatrix */ - GPU_UNIFORM_PROJECTION, /* mat4 ProjectionMatrix */ - GPU_UNIFORM_VIEWPROJECTION, /* mat4 ViewProjectionMatrix */ - GPU_UNIFORM_MVP, /* mat4 ModelViewProjectionMatrix */ - - GPU_UNIFORM_MODEL_INV, /* mat4 ModelMatrixInverse */ - GPU_UNIFORM_VIEW_INV, /* mat4 ViewMatrixInverse */ - GPU_UNIFORM_MODELVIEW_INV, /* mat4 ModelViewMatrixInverse */ - GPU_UNIFORM_PROJECTION_INV, /* mat4 ProjectionMatrixInverse */ - GPU_UNIFORM_VIEWPROJECTION_INV, /* mat4 ViewProjectionMatrixInverse */ - - GPU_UNIFORM_NORMAL, /* mat3 NormalMatrix */ - GPU_UNIFORM_ORCO, /* vec4 OrcoTexCoFactors[] */ - GPU_UNIFORM_CLIPPLANES, /* vec4 WorldClipPlanes[] */ - - GPU_UNIFORM_COLOR, /* vec4 color */ - GPU_UNIFORM_BASE_INSTANCE, /* int baseInstance */ - GPU_UNIFORM_RESOURCE_CHUNK, /* int resourceChunk */ - GPU_UNIFORM_RESOURCE_ID, /* int resourceId */ - GPU_UNIFORM_SRGB_TRANSFORM, /* bool srgbTarget */ - - GPU_NUM_UNIFORMS, /* Special value, denotes number of builtin uniforms. */ -} GPUUniformBuiltin; - -typedef enum { - GPU_UNIFORM_BLOCK_VIEW = 0, /* viewBlock */ - GPU_UNIFORM_BLOCK_MODEL, /* modelBlock */ - GPU_UNIFORM_BLOCK_INFO, /* infoBlock */ - - GPU_NUM_UNIFORM_BLOCKS, /* Special value, denotes number of builtin uniforms block. */ -} GPUUniformBlockBuiltin; - -typedef struct GPUShaderInput { - uint32_t name_offset; - uint32_t name_hash; - int32_t location; - /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */ - int32_t binding; -} GPUShaderInput; - -#define GPU_SHADERINTERFACE_REF_ALLOC_COUNT 16 - -typedef struct GPUShaderInterface { - /** Buffer containing all inputs names separated by '\0'. */ - char *name_buffer; - /** Reference to GPUBatches using this interface */ - void **batches; - uint batches_len; - /** Input counts. */ - uint attribute_len; - uint ubo_len; - uint uniform_len; - /** Enabled bindpoints that needs to be fed with data. */ - uint16_t enabled_attr_mask; - uint16_t enabled_ubo_mask; - uint64_t enabled_tex_mask; - /** Opengl Location of builtin uniforms. Fast access, no lookup needed. */ - int32_t builtins[GPU_NUM_UNIFORMS]; - int32_t builtin_blocks[GPU_NUM_UNIFORM_BLOCKS]; - /** Flat array. In this order: Attributes, Ubos, Uniforms. */ - GPUShaderInput inputs[0]; -} GPUShaderInterface; - -GPUShaderInterface *GPU_shaderinterface_create(int32_t program_id); -void GPU_shaderinterface_discard(GPUShaderInterface *); - -const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *, const char *name); -int32_t GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface, - GPUUniformBuiltin builtin); -int32_t GPU_shaderinterface_block_builtin(const GPUShaderInterface *shaderface, - GPUUniformBlockBuiltin builtin); -const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *, const char *name); -const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *, const char *name); - -/* keep track of batches using this interface */ -void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *interface, void *cache); -void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *interface, void *cache); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index be3250f6654..253877bcca0 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -29,6 +29,7 @@ typedef enum eGPUWriteMask { GPU_WRITE_BLUE = (1 << 2), GPU_WRITE_ALPHA = (1 << 3), GPU_WRITE_DEPTH = (1 << 4), + GPU_WRITE_STENCIL = (1 << 5), GPU_WRITE_COLOR = (GPU_WRITE_RED | GPU_WRITE_GREEN | GPU_WRITE_BLUE | GPU_WRITE_ALPHA), } eGPUWriteMask; @@ -66,9 +67,9 @@ typedef enum eGPUBlend { typedef enum eGPUDepthTest { GPU_DEPTH_NONE = 0, - GPU_DEPTH_ALWAYS, + GPU_DEPTH_ALWAYS, /* Used to draw to the depth buffer without really testing. */ GPU_DEPTH_LESS, - GPU_DEPTH_LESS_EQUAL, + GPU_DEPTH_LESS_EQUAL, /* Default. */ GPU_DEPTH_EQUAL, GPU_DEPTH_GREATER, GPU_DEPTH_GREATER_EQUAL, @@ -106,11 +107,11 @@ extern "C" { void GPU_blend(eGPUBlend blend); void GPU_face_culling(eGPUFaceCullTest culling); -void GPU_front_facing(bool invert); +void GPU_depth_test(eGPUDepthTest test); +void GPU_stencil_test(eGPUStencilTest test); void GPU_provoking_vertex(eGPUProvokingVertex vert); +void GPU_front_facing(bool invert); void GPU_depth_range(float near, float far); -void GPU_depth_test(bool enable); -bool GPU_depth_test_enabled(void); void GPU_scissor_test(bool enable); void GPU_line_smooth(bool enable); void GPU_line_width(float width); @@ -144,7 +145,10 @@ void GPU_stencil_write_mask_set(uint write_mask); void GPU_stencil_compare_mask_set(uint compare_mask); eGPUBlend GPU_blend_get(void); +eGPUDepthTest GPU_depth_test_get(void); eGPUWriteMask GPU_write_mask_get(void); +uint GPU_stencil_mask_get(void); +eGPUStencilTest GPU_stencil_test_get(void); void GPU_flush(void); void GPU_finish(void); diff --git a/source/blender/gpu/GPU_uniform_buffer.h b/source/blender/gpu/GPU_uniform_buffer.h new file mode 100644 index 00000000000..4a00dda634d --- /dev/null +++ b/source/blender/gpu/GPU_uniform_buffer.h @@ -0,0 +1,61 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * Uniform buffers API. Used to handle many uniforms update at once. + * Make sure that the data structure is compatible with what the implementation expect. + * (see "7.6.2.2 Standard Uniform Block Layout" from the OpenGL spec for more info about std140 + * layout) + * Rule of thumb: Padding to 16bytes, don't use vec3, don't use arrays of anything that is not vec4 + * aligned . + */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +struct ListBase; + +/** Opaque pointer hiding blender::gpu::UniformBuf. */ +typedef struct GPUUniformBuf { + void *dummy; +} GPUUniformBuf; + +GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name); +GPUUniformBuf *GPU_uniformbuf_create_from_list(struct ListBase *inputs, const char *name); + +#define GPU_uniformbuf_create(size) GPU_uniformbuf_create_ex(size, NULL, __func__); + +void GPU_uniformbuf_free(GPUUniformBuf *ubo); + +void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data); + +void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int number); +void GPU_uniformbuf_unbind(GPUUniformBuf *ubo); +void GPU_uniformbuf_unbind_all(void); + +#define GPU_UBO_BLOCK_NAME "nodeTree" + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/gpu/GPU_uniformbuffer.h b/source/blender/gpu/GPU_uniformbuffer.h deleted file mode 100644 index e2b2a757fb9..00000000000 --- a/source/blender/gpu/GPU_uniformbuffer.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2005 Blender Foundation. - * All rights reserved. - */ - -/** \file - * \ingroup gpu - */ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -struct ListBase; - -typedef struct GPUUniformBuffer GPUUniformBuffer; - -GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]); -GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(struct ListBase *inputs, char err_out[256]); - -void GPU_uniformbuffer_free(GPUUniformBuffer *ubo); - -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data); -void GPU_uniformbuffer_dynamic_update(GPUUniformBuffer *ubo_); - -void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number); -void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo); -void GPU_uniformbuffer_unbind_all(void); - -bool GPU_uniformbuffer_is_empty(GPUUniformBuffer *ubo); -bool GPU_uniformbuffer_is_dirty(GPUUniformBuffer *ubo); - -#define GPU_UBO_BLOCK_NAME "nodeTree" - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index 60b78ecd59b..c3e2f1788b4 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -55,8 +55,8 @@ typedef struct ViewportMemoryPool { struct BLI_memblock *views; struct BLI_memblock *passes; struct BLI_memblock *images; - struct GPUUniformBuffer **matrices_ubo; - struct GPUUniformBuffer **obinfos_ubo; + struct GPUUniformBuf **matrices_ubo; + struct GPUUniformBuf **obinfos_ubo; uint ubo_len; } ViewportMemoryPool; diff --git a/source/blender/gpu/intern/gpu_attr_binding.cc b/source/blender/gpu/intern/gpu_attr_binding.cc index 6cb60884620..2a48107e190 100644 --- a/source/blender/gpu/intern/gpu_attr_binding.cc +++ b/source/blender/gpu/intern/gpu_attr_binding.cc @@ -61,9 +61,7 @@ static void write_attr_location(GPUAttrBinding *binding, uint a_idx, uint locati binding->enabled_bits |= 1 << a_idx; } -void get_attr_locations(const GPUVertFormat *format, - GPUAttrBinding *binding, - const GPUShaderInterface *shaderface) +void get_attr_locations(const GPUVertFormat *format, GPUAttrBinding *binding, GPUShader *shader) { AttrBinding_clear(binding); @@ -71,13 +69,12 @@ void get_attr_locations(const GPUVertFormat *format, const GPUVertAttr *a = &format->attrs[a_idx]; for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); - const GPUShaderInput *input = GPU_shaderinterface_attr(shaderface, name); -#if TRUST_NO_ONE - assert(input != NULL); + int loc = GPU_shader_get_attribute(shader, name); /* TODO: make this a recoverable runtime error? * indicates mismatch between vertex format and program. */ -#endif - write_attr_location(binding, a_idx, input->location); + BLI_assert(loc != -1); + + write_attr_location(binding, a_idx, loc); } } } diff --git a/source/blender/gpu/intern/gpu_attr_binding_private.h b/source/blender/gpu/intern/gpu_attr_binding_private.h index 4d359343c38..cd67a51a822 100644 --- a/source/blender/gpu/intern/gpu_attr_binding_private.h +++ b/source/blender/gpu/intern/gpu_attr_binding_private.h @@ -25,8 +25,8 @@ #pragma once -#include "GPU_shader_interface.h" #include "GPU_vertex_format.h" +#include "gpu_shader_interface.hh" #ifdef __cplusplus extern "C" { @@ -35,9 +35,7 @@ extern "C" { /* TODO(fclem) remove, use shaderface directly. */ void AttrBinding_clear(GPUAttrBinding *binding); -void get_attr_locations(const GPUVertFormat *format, - GPUAttrBinding *binding, - const GPUShaderInterface *shaderface); +void get_attr_locations(const GPUVertFormat *format, GPUAttrBinding *binding, GPUShader *shader); uint read_attr_location(const GPUAttrBinding *binding, uint a_idx); #ifdef __cplusplus diff --git a/source/blender/gpu/intern/gpu_backend.hh b/source/blender/gpu/intern/gpu_backend.hh index 6ab0e32a754..f63f3cead2b 100644 --- a/source/blender/gpu/intern/gpu_backend.hh +++ b/source/blender/gpu/intern/gpu_backend.hh @@ -25,14 +25,16 @@ #pragma once -#include "gpu_batch_private.hh" -#include "gpu_context_private.hh" -#include "gpu_drawlist_private.hh" -#include "gpu_shader_private.hh" +struct GPUContext; namespace blender { namespace gpu { +class Batch; +class DrawList; +class Shader; +class UniformBuf; + class GPUBackend { public: virtual ~GPUBackend(){}; @@ -46,6 +48,7 @@ class GPUBackend { // virtual FrameBuffer *framebuffer_alloc(void) = 0; virtual Shader *shader_alloc(const char *name) = 0; // virtual Texture *texture_alloc(void) = 0; + virtual UniformBuf *uniformbuf_alloc(int size, const char *name) = 0; }; } // namespace gpu diff --git a/source/blender/gpu/intern/gpu_batch_private.hh b/source/blender/gpu/intern/gpu_batch_private.hh index 3a8044efc1d..c0444647fe1 100644 --- a/source/blender/gpu/intern/gpu_batch_private.hh +++ b/source/blender/gpu/intern/gpu_batch_private.hh @@ -28,11 +28,14 @@ #include "GPU_batch.h" #include "GPU_context.h" -#include "GPU_shader_interface.h" namespace blender { namespace gpu { +/** + * Base class which is then specialized for each implementation (GL, VK, ...). + * NOTE: Extends GPUBatch as we still needs to expose some of the internals to the outside C code. + **/ class Batch : public GPUBatch { public: Batch(){}; diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index b051d4fe59a..1629584e841 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -43,7 +43,7 @@ #include "GPU_extensions.h" #include "GPU_material.h" #include "GPU_shader.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_vertex_format.h" #include "BLI_sys_types.h" /* for intptr_t support */ diff --git a/source/blender/gpu/intern/gpu_context_private.hh b/source/blender/gpu/intern/gpu_context_private.hh index b774d6b0995..e8c9c976e9a 100644 --- a/source/blender/gpu/intern/gpu_context_private.hh +++ b/source/blender/gpu/intern/gpu_context_private.hh @@ -29,6 +29,7 @@ #include "GPU_context.h" +#include "gpu_shader_private.hh" #include "gpu_state_private.hh" #include <mutex> @@ -43,7 +44,7 @@ struct GPUMatrixState; struct GPUContext { public: /** State managment */ - GPUShader *shader = NULL; + blender::gpu::Shader *shader = NULL; GPUFrameBuffer *current_fbo = NULL; GPUMatrixState *matrix_state = NULL; blender::gpu::GPUStateManager *state_manager = NULL; diff --git a/source/blender/gpu/intern/gpu_drawlist_private.hh b/source/blender/gpu/intern/gpu_drawlist_private.hh index 04cc18a5ffd..ddb09fb0c89 100644 --- a/source/blender/gpu/intern/gpu_drawlist_private.hh +++ b/source/blender/gpu/intern/gpu_drawlist_private.hh @@ -28,6 +28,10 @@ namespace blender { namespace gpu { +/** + * Implementation of Multi Draw Indirect. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ class DrawList { public: virtual ~DrawList(){}; diff --git a/source/blender/gpu/intern/gpu_extensions.cc b/source/blender/gpu/intern/gpu_extensions.cc index 8074e4b64f0..1d607d79b01 100644 --- a/source/blender/gpu/intern/gpu_extensions.cc +++ b/source/blender/gpu/intern/gpu_extensions.cc @@ -71,12 +71,10 @@ static struct GPUGlobal { GLint maxubosize; GLint maxubobinds; int samples_color_texture_max; - float line_width_range[2]; /* workaround for different calculation of dfdy factors on GPUs. Some GPUs/drivers * calculate dfdy in shader differently when drawing to an off-screen buffer. First * number is factor on screen and second is off-screen */ float dfdyfactors[2]; - float max_anisotropy; /* Some Intel drivers have limited support for `GLEW_ARB_base_instance` so in * these cases it is best to indicate that it is not supported. See T67951 */ bool glew_arb_base_instance_is_supported; @@ -164,11 +162,6 @@ int GPU_max_textures_vert(void) return GG.maxtexturesvert; } -float GPU_max_texture_anisotropy(void) -{ - return GG.max_anisotropy; -} - int GPU_max_color_texture_samples(void) { return GG.samples_color_texture_max; @@ -189,11 +182,6 @@ int GPU_max_ubo_size(void) return GG.maxubosize; } -float GPU_max_line_width(void) -{ - return GG.line_width_range[1]; -} - void GPU_get_dfdy_factors(float fac[2]) { copy_v2_v2(fac, GG.dfdyfactors); @@ -264,18 +252,9 @@ void gpu_extensions_init(void) glGetIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &GG.maxtexlayers); glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &GG.maxcubemapsize); - if (GLEW_EXT_texture_filter_anisotropic) { - glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &GG.max_anisotropy); - } - else { - GG.max_anisotropy = 1.0f; - } - glGetIntegerv(GL_MAX_FRAGMENT_UNIFORM_BLOCKS, &GG.maxubobinds); glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, &GG.maxubosize); - glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, GG.line_width_range); - glGetIntegerv(GL_MAX_COLOR_TEXTURE_SAMPLES, &GG.samples_color_texture_max); const char *vendor = (const char *)glGetString(GL_VENDOR); diff --git a/source/blender/gpu/intern/gpu_framebuffer.cc b/source/blender/gpu/intern/gpu_framebuffer.cc index da8ab80b347..88013640bfc 100644 --- a/source/blender/gpu/intern/gpu_framebuffer.cc +++ b/source/blender/gpu/intern/gpu_framebuffer.cc @@ -622,23 +622,37 @@ void GPU_framebuffer_clear(GPUFrameBuffer *fb, { CHECK_FRAMEBUFFER_IS_BOUND(fb); - GPU_context_active_get()->state_manager->apply_state(); + /* Save and restore the state. */ + eGPUWriteMask write_mask = GPU_write_mask_get(); + uint stencil_mask = GPU_stencil_mask_get(); + eGPUStencilTest stencil_test = GPU_stencil_test_get(); if (buffers & GPU_COLOR_BIT) { - glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + GPU_color_mask(true, true, true, true); glClearColor(clear_col[0], clear_col[1], clear_col[2], clear_col[3]); } if (buffers & GPU_DEPTH_BIT) { - glDepthMask(GL_TRUE); + GPU_depth_mask(true); glClearDepth(clear_depth); } if (buffers & GPU_STENCIL_BIT) { - glStencilMask(0xFF); + GPU_stencil_write_mask_set(0xFFu); + GPU_stencil_test(GPU_STENCIL_ALWAYS); glClearStencil(clear_stencil); } + GPU_context_active_get()->state_manager->apply_state(); + GLbitfield mask = convert_buffer_bits_to_gl(buffers); glClear(mask); + + if (buffers & (GPU_COLOR_BIT | GPU_DEPTH_BIT)) { + GPU_write_mask(write_mask); + } + if (buffers & GPU_STENCIL_BIT) { + GPU_stencil_write_mask_set(stencil_mask); + GPU_stencil_test(stencil_test); + } } /* Clear all textures bound to this framebuffer with a different color. */ @@ -1103,18 +1117,22 @@ void GPU_offscreen_viewport_data_get(GPUOffScreen *ofs, void GPU_clear_color(float red, float green, float blue, float alpha) { + BLI_assert((GPU_write_mask_get() & GPU_WRITE_COLOR) != 0); + + GPU_context_active_get()->state_manager->apply_state(); + glClearColor(red, green, blue, alpha); + glClear(GL_COLOR_BUFFER_BIT); } void GPU_clear_depth(float depth) { - glClearDepth(depth); -} + BLI_assert((GPU_write_mask_get() & GPU_WRITE_DEPTH) != 0); -void GPU_clear(eGPUFrameBufferBits flags) -{ GPU_context_active_get()->state_manager->apply_state(); - glClear(convert_buffer_bits_to_gl(flags)); + + glClearDepth(depth); + glClear(GL_DEPTH_BUFFER_BIT); } void GPU_frontbuffer_read_pixels( diff --git a/source/blender/gpu/intern/gpu_immediate.cc b/source/blender/gpu/intern/gpu_immediate.cc index dd05689d69a..431dbe848f7 100644 --- a/source/blender/gpu/intern/gpu_immediate.cc +++ b/source/blender/gpu/intern/gpu_immediate.cc @@ -73,7 +73,6 @@ typedef struct { GLuint vao_id; GPUShader *bound_program; - const GPUShaderInterface *shader_interface; GPUAttrBinding attr_binding; uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */ } Immediate; @@ -148,14 +147,13 @@ void immBindShader(GPUShader *shader) BLI_assert(imm.bound_program == NULL); imm.bound_program = shader; - imm.shader_interface = shader->interface; if (!imm.vertex_format.packed) { VertexFormat_pack(&imm.vertex_format); } GPU_shader_bind(shader); - get_attr_locations(&imm.vertex_format, &imm.attr_binding, imm.shader_interface); + get_attr_locations(&imm.vertex_format, &imm.attr_binding, shader); GPU_matrix_bind(shader); GPU_shader_set_srgb_uniform(shader); } @@ -749,123 +747,77 @@ void immVertex2iv(uint attr_id, const int data[2]) /* --- generic uniform functions --- */ -#if 0 -# if TRUST_NO_ONE -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); \ - assert(uniform); -# else -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); -# endif -#else -/* NOTE: It is possible to have uniform fully optimized out from the shader. - * In this case we can't assert failure or allow NULL-pointer dereference. - * TODO(sergey): How can we detect existing-but-optimized-out uniform but still - * catch typos in uniform names passed to immUniform*() functions? */ -# define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(imm.shader_interface, name); \ - if (uniform == NULL) \ - return; -#endif - void immUniform1f(const char *name, float x) { - GET_UNIFORM - glUniform1f(uniform->location, x); + GPU_shader_uniform_1f(imm.bound_program, name, x); } void immUniform2f(const char *name, float x, float y) { - GET_UNIFORM - glUniform2f(uniform->location, x, y); + GPU_shader_uniform_2f(imm.bound_program, name, x, y); } void immUniform2fv(const char *name, const float data[2]) { - GET_UNIFORM - glUniform2fv(uniform->location, 1, data); + GPU_shader_uniform_2fv(imm.bound_program, name, data); } void immUniform3f(const char *name, float x, float y, float z) { - GET_UNIFORM - glUniform3f(uniform->location, x, y, z); + GPU_shader_uniform_3f(imm.bound_program, name, x, y, z); } void immUniform3fv(const char *name, const float data[3]) { - GET_UNIFORM - glUniform3fv(uniform->location, 1, data); -} - -/* can increase this limit or move to another file */ -#define MAX_UNIFORM_NAME_LEN 60 - -/* Note array index is not supported for name (i.e: "array[0]"). */ -void immUniformArray3fv(const char *name, const float *data, int count) -{ - GET_UNIFORM - glUniform3fv(uniform->location, count, data); + GPU_shader_uniform_3fv(imm.bound_program, name, data); } void immUniform4f(const char *name, float x, float y, float z, float w) { - GET_UNIFORM - glUniform4f(uniform->location, x, y, z, w); + GPU_shader_uniform_4f(imm.bound_program, name, x, y, z, w); } void immUniform4fv(const char *name, const float data[4]) { - GET_UNIFORM - glUniform4fv(uniform->location, 1, data); + GPU_shader_uniform_4fv(imm.bound_program, name, data); } /* Note array index is not supported for name (i.e: "array[0]"). */ void immUniformArray4fv(const char *name, const float *data, int count) { - GET_UNIFORM - glUniform4fv(uniform->location, count, data); + GPU_shader_uniform_4fv_array(imm.bound_program, name, count, (float(*)[4])data); } void immUniformMatrix4fv(const char *name, const float data[4][4]) { - GET_UNIFORM - glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (float *)data); + GPU_shader_uniform_mat4(imm.bound_program, name, data); } void immUniform1i(const char *name, int x) { - GET_UNIFORM - glUniform1i(uniform->location, x); -} - -void immUniform4iv(const char *name, const int data[4]) -{ - GET_UNIFORM - glUniform4iv(uniform->location, 1, data); + GPU_shader_uniform_1i(imm.bound_program, name, x); } void immBindTexture(const char *name, GPUTexture *tex) { - GET_UNIFORM - GPU_texture_bind(tex, uniform->binding); + int binding = GPU_shader_get_texture_binding(imm.bound_program, name); + GPU_texture_bind(tex, binding); } void immBindTextureSampler(const char *name, GPUTexture *tex, eGPUSamplerState state) { - GET_UNIFORM - GPU_texture_bind_ex(tex, state, uniform->binding, true); + int binding = GPU_shader_get_texture_binding(imm.bound_program, name); + GPU_texture_bind_ex(tex, state, binding, true); } /* --- convenience functions for setting "uniform vec4 color" --- */ void immUniformColor4f(float r, float g, float b, float a) { - int32_t uniform_loc = GPU_shaderinterface_uniform_builtin(imm.shader_interface, - GPU_UNIFORM_COLOR); + int32_t uniform_loc = GPU_shader_get_builtin_uniform(imm.bound_program, GPU_UNIFORM_COLOR); BLI_assert(uniform_loc != -1); - glUniform4f(uniform_loc, r, g, b, a); + float data[4] = {r, g, b, a}; + GPU_shader_uniform_vector(imm.bound_program, uniform_loc, 4, 1, data); } void immUniformColor4fv(const float rgba[4]) diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 8df1f94238a..1016e766140 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -47,7 +47,7 @@ #include "GPU_material.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "DRW_engine.h" @@ -88,11 +88,11 @@ struct GPUMaterial { eGPUMatFlag flag; /* Used by 2.8 pipeline */ - GPUUniformBuffer *ubo; /* UBOs for shader uniforms. */ + GPUUniformBuf *ubo; /* UBOs for shader uniforms. */ /* Eevee SSS */ - GPUUniformBuffer *sss_profile; /* UBO containing SSS profile. */ - GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ + GPUUniformBuf *sss_profile; /* UBO containing SSS profile. */ + GPUTexture *sss_tex_profile; /* Texture containing SSS profile. */ float sss_enabled; float sss_radii[3]; int sss_samples; @@ -174,13 +174,13 @@ static void gpu_material_free_single(GPUMaterial *material) GPU_pass_release(material->pass); } if (material->ubo != NULL) { - GPU_uniformbuffer_free(material->ubo); + GPU_uniformbuf_free(material->ubo); } if (material->sss_tex_profile != NULL) { GPU_texture_free(material->sss_tex_profile); } if (material->sss_profile != NULL) { - GPU_uniformbuffer_free(material->sss_profile); + GPU_uniformbuf_free(material->sss_profile); } if (material->coba_tex != NULL) { GPU_texture_free(material->coba_tex); @@ -220,7 +220,7 @@ Material *GPU_material_get_material(GPUMaterial *material) return material->ma; } -GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material) +GPUUniformBuf *GPU_material_uniform_buffer_get(GPUMaterial *material) { return material->ubo; } @@ -232,7 +232,12 @@ GPUUniformBuffer *GPU_material_uniform_buffer_get(GPUMaterial *material) */ void GPU_material_uniform_buffer_create(GPUMaterial *material, ListBase *inputs) { - material->ubo = GPU_uniformbuffer_dynamic_create(inputs, NULL); +#ifndef NDEBUG + const char *name = material->name; +#else + const char *name = "Material"; +#endif + material->ubo = GPU_uniformbuf_create_from_list(inputs, name); } /* Eevee Subsurface scattering. */ @@ -507,13 +512,13 @@ void GPU_material_sss_profile_create(GPUMaterial *material, /* Update / Create UBO */ if (material->sss_profile == NULL) { - material->sss_profile = GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL); + material->sss_profile = GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } } -struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, - int sample_len, - GPUTexture **tex_profile) +struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, + int sample_len, + GPUTexture **tex_profile) { if (!material->sss_enabled) { return NULL; @@ -530,7 +535,7 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, compute_sss_kernel(&kd, material->sss_radii, sample_len, material->sss_falloff, sharpness); /* Update / Create UBO */ - GPU_uniformbuffer_update(material->sss_profile, &kd); + GPU_uniformbuf_update(material->sss_profile, &kd); /* Update / Create Tex */ float *translucence_profile; @@ -555,9 +560,9 @@ struct GPUUniformBuffer *GPU_material_sss_profile_get(GPUMaterial *material, return material->sss_profile; } -struct GPUUniformBuffer *GPU_material_create_sss_profile_ubo(void) +struct GPUUniformBuf *GPU_material_create_sss_profile_ubo(void) { - return GPU_uniformbuffer_create(sizeof(GPUSssKernelData), NULL, NULL); + return GPU_uniformbuf_create(sizeof(GPUSssKernelData)); } #undef SSS_EXPONENT @@ -735,7 +740,7 @@ GPUMaterial *GPU_material_from_nodetree(Scene *scene, gpu_node_graph_free(&mat->graph); } - /* Only free after GPU_pass_shader_get where GPUUniformBuffer + /* Only free after GPU_pass_shader_get where GPUUniformBuf * read data from the local tree. */ ntreeFreeLocalTree(localtree); MEM_freeN(localtree); diff --git a/source/blender/gpu/intern/gpu_matrix.cc b/source/blender/gpu/intern/gpu_matrix.cc index 951652b9393..cdb6d303588 100644 --- a/source/blender/gpu/intern/gpu_matrix.cc +++ b/source/blender/gpu/intern/gpu_matrix.cc @@ -21,8 +21,6 @@ * \ingroup gpu */ -#include "GPU_shader_interface.h" - #include "gpu_context_private.hh" #include "gpu_matrix_private.h" @@ -649,14 +647,13 @@ void GPU_matrix_bind(GPUShader *shader) * call this before a draw call if desired matrices are dirty * call glUseProgram before this, as glUniform expects program to be bound */ - const GPUShaderInterface *shaderface = shader->interface; - int32_t MV = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW); - int32_t P = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION); - int32_t MVP = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MVP); - - int32_t N = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_NORMAL); - int32_t MV_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_MODELVIEW_INV); - int32_t P_inv = GPU_shaderinterface_uniform_builtin(shaderface, GPU_UNIFORM_PROJECTION_INV); + int32_t MV = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW); + int32_t P = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION); + int32_t MVP = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MVP); + + int32_t N = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_NORMAL); + int32_t MV_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_MODELVIEW_INV); + int32_t P_inv = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_PROJECTION_INV); if (MV != -1) { GPU_shader_uniform_vector(shader, MV, 16, 1, (const float *)GPU_matrix_model_view_get(NULL)); diff --git a/source/blender/gpu/intern/gpu_node_graph.c b/source/blender/gpu/intern/gpu_node_graph.c index 81cf2d69f4d..1b8a5e20240 100644 --- a/source/blender/gpu/intern/gpu_node_graph.c +++ b/source/blender/gpu/intern/gpu_node_graph.c @@ -592,10 +592,10 @@ bool GPU_stack_link(GPUMaterial *material, return true; } -GPUNodeLink *GPU_uniformbuffer_link_out(GPUMaterial *mat, - bNode *node, - GPUNodeStack *stack, - const int index) +GPUNodeLink *GPU_uniformbuf_link_out(GPUMaterial *mat, + bNode *node, + GPUNodeStack *stack, + const int index) { return gpu_uniformbuffer_link(mat, node, stack, index, SOCK_OUT); } diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index 29e2615345c..c3ccb68a998 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -27,6 +27,7 @@ #include <stdlib.h> #include <string.h> +#include "GPU_framebuffer.h" #include "GPU_glew.h" #include "GPU_immediate.h" #include "GPU_select.h" @@ -287,7 +288,7 @@ typedef struct GPUPickState { int viewport[4]; int scissor[4]; eGPUWriteMask write_mask; - bool depth_test; + eGPUDepthTest depth_test; } GPUPickState; static GPUPickState g_pick_state = {0}; @@ -311,18 +312,17 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c /* Restrict OpenGL operations for when we don't have cache */ if (ps->is_cached == false) { ps->write_mask = GPU_write_mask_get(); - ps->depth_test = GPU_depth_test_enabled(); + ps->depth_test = GPU_depth_test_get(); GPU_scissor_get(ps->scissor); /* disable writing to the framebuffer */ GPU_color_mask(false, false, false, false); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); + GPU_depth_mask(true); /* Always use #GL_LEQUAL even though GPU_SELECT_PICK_ALL always clears the buffer. This is * because individual objects themselves might have sections that overlap and we need these * to have the correct distance information. */ - glDepthFunc(GL_LEQUAL); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); float viewport[4]; GPU_viewport_size_get_f(viewport); @@ -339,7 +339,7 @@ void gpu_select_pick_begin(uint (*buffer)[4], uint bufsize, const rcti *input, c /* It's possible we don't want to clear depth buffer, * so existing elements are masked by current z-buffer. */ - glClear(GL_DEPTH_BUFFER_BIT); + GPU_clear_depth(1.0f); /* scratch buffer (read new values here) */ ps->gl.rect_depth_test = depth_buf_malloc(rect_len); @@ -518,8 +518,13 @@ bool gpu_select_pick_load_id(uint id, bool end) SWAP(DepthBufCache *, ps->gl.rect_depth, ps->gl.rect_depth_test); if (g_pick_state.mode == GPU_SELECT_PICK_ALL) { + /* (fclem) This is to be on the safe side. I don't know if this is required. */ + bool prev_depth_mask = GPU_depth_mask_get(); /* we want new depths every time */ - glClear(GL_DEPTH_BUFFER_BIT); + GPU_depth_mask(true); + GPU_clear_depth(1.0f); + + GPU_depth_mask(prev_depth_mask); } } } diff --git a/source/blender/gpu/intern/gpu_select_sample_query.c b/source/blender/gpu/intern/gpu_select_sample_query.c index 62414febb44..45d52b22664 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.c +++ b/source/blender/gpu/intern/gpu_select_sample_query.c @@ -26,6 +26,7 @@ #include <stdlib.h> +#include "GPU_framebuffer.h" #include "GPU_glew.h" #include "GPU_select.h" #include "GPU_state.h" @@ -65,7 +66,7 @@ typedef struct GPUQueryState { int viewport[4]; int scissor[4]; eGPUWriteMask write_mask; - bool depth_test; + eGPUDepthTest depth_test; } GPUQueryState; static GPUQueryState g_query_state = {0}; @@ -91,41 +92,41 @@ void gpu_select_query_begin( glGenQueries(g_query_state.num_of_queries, g_query_state.queries); g_query_state.write_mask = GPU_write_mask_get(); - g_query_state.depth_test = GPU_depth_test_enabled(); + g_query_state.depth_test = GPU_depth_test_get(); GPU_scissor_get(g_query_state.scissor); + GPU_viewport_size_get_i(g_query_state.viewport); - /* disable writing to the framebuffer */ - GPU_color_mask(false, false, false, false); + /* Write to color buffer. Seems to fix issues with selecting alpha blended geom (see T7997). */ + GPU_color_mask(true, true, true, true); /* In order to save some fill rate we minimize the viewport using rect. * We need to get the region of the viewport so that our geometry doesn't * get rejected before the depth test. Should probably cull rect against * the viewport but this is a rare case I think */ - GPU_viewport_size_get_i(g_query_state.viewport); - GPU_viewport(g_query_state.viewport[0], - g_query_state.viewport[1], - BLI_rcti_size_x(input), - BLI_rcti_size_y(input)); + + int viewport[4] = { + UNPACK2(g_query_state.viewport), BLI_rcti_size_x(input), BLI_rcti_size_y(input)}; + + GPU_viewport(UNPACK4(viewport)); + GPU_scissor(UNPACK4(viewport)); + GPU_scissor_test(false); /* occlusion queries operates on fragments that pass tests and since we are interested on all * objects in the view frustum independently of their order, we need to disable the depth test */ if (mode == GPU_SELECT_ALL) { /* glQueries on Windows+Intel drivers only works with depth testing turned on. * See T62947 for details */ - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_ALWAYS); - glDepthMask(GL_TRUE); + GPU_depth_test(GPU_DEPTH_ALWAYS); + GPU_depth_mask(true); } else if (mode == GPU_SELECT_NEAREST_FIRST_PASS) { - glClear(GL_DEPTH_BUFFER_BIT); - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_TRUE); - glDepthFunc(GL_LEQUAL); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_depth_mask(true); + GPU_clear_depth(1.0f); } else if (mode == GPU_SELECT_NEAREST_SECOND_PASS) { - glEnable(GL_DEPTH_TEST); - glDepthMask(GL_FALSE); - glDepthFunc(GL_EQUAL); + GPU_depth_test(GPU_DEPTH_EQUAL); + GPU_depth_mask(false); } } diff --git a/source/blender/gpu/intern/gpu_shader.cc b/source/blender/gpu/intern/gpu_shader.cc index 536396ad3c6..b1772bed6e8 100644 --- a/source/blender/gpu/intern/gpu_shader.cc +++ b/source/blender/gpu/intern/gpu_shader.cc @@ -41,7 +41,7 @@ #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "gpu_backend.hh" #include "gpu_context_private.hh" @@ -52,6 +52,11 @@ extern "C" char datatoc_gpu_shader_colorspace_lib_glsl[]; using namespace blender; using namespace blender::gpu; +/** Opaque type hidding blender::gpu::Shader */ +struct GPUShader { + char _pad[1]; +}; + /* -------------------------------------------------------------------- */ /** \name Debug functions * \{ */ @@ -196,9 +201,7 @@ Shader::Shader(const char *sh_name) Shader::~Shader() { - if (this->interface) { - GPU_shaderinterface_discard(this->interface); - } + delete interface; } static void standard_defines(Vector<const char *> &sources) @@ -304,12 +307,12 @@ GPUShader *GPU_shader_create_ex(const char *vertcode, return NULL; }; - return static_cast<GPUShader *>(shader); + return reinterpret_cast<GPUShader *>(shader); } void GPU_shader_free(GPUShader *shader) { - delete static_cast<Shader *>(shader); + delete reinterpret_cast<Shader *>(shader); } /** \} */ @@ -345,7 +348,7 @@ GPUShader *GPU_shader_create_from_python(const char *vertcode, } GPUShader *sh = GPU_shader_create_ex( - vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, NULL); + vertcode, fragcode, geomcode, libcode, defines, GPU_SHADER_TFB_NONE, NULL, 0, "pyGPUShader"); MEM_SAFE_FREE(libcodecat); return sh; @@ -431,19 +434,19 @@ struct GPUShader *GPU_shader_create_from_arrays_impl( void GPU_shader_bind(GPUShader *gpu_shader) { - Shader *shader = static_cast<Shader *>(gpu_shader); + Shader *shader = reinterpret_cast<Shader *>(gpu_shader); GPUContext *ctx = GPU_context_active_get(); if (ctx->shader != shader) { ctx->shader = shader; shader->bind(); - GPU_matrix_bind(shader); - GPU_shader_set_srgb_uniform(shader); + GPU_matrix_bind(gpu_shader); + GPU_shader_set_srgb_uniform(gpu_shader); } if (GPU_matrix_dirty_get()) { - GPU_matrix_bind(shader); + GPU_matrix_bind(gpu_shader); } } @@ -452,7 +455,7 @@ void GPU_shader_unbind(void) #ifndef NDEBUG GPUContext *ctx = GPU_context_active_get(); if (ctx->shader) { - static_cast<Shader *>(ctx->shader)->unbind(); + reinterpret_cast<Shader *>(ctx->shader)->unbind(); } ctx->shader = NULL; #endif @@ -468,12 +471,12 @@ void GPU_shader_unbind(void) bool GPU_shader_transform_feedback_enable(GPUShader *shader, GPUVertBuf *vertbuf) { - return static_cast<Shader *>(shader)->transform_feedback_enable(vertbuf); + return reinterpret_cast<Shader *>(shader)->transform_feedback_enable(vertbuf); } void GPU_shader_transform_feedback_disable(GPUShader *shader) { - static_cast<Shader *>(shader)->transform_feedback_disable(); + reinterpret_cast<Shader *>(shader)->transform_feedback_disable(); } /** \} */ @@ -484,43 +487,49 @@ void GPU_shader_transform_feedback_disable(GPUShader *shader) int GPU_shader_get_uniform(GPUShader *shader, const char *name) { - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *uniform = interface->uniform_get(name); return uniform ? uniform->location : -1; } int GPU_shader_get_builtin_uniform(GPUShader *shader, int builtin) { - return GPU_shaderinterface_uniform_builtin(shader->interface, - static_cast<GPUUniformBuiltin>(builtin)); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + return interface->uniform_builtin((GPUUniformBuiltin)builtin); } int GPU_shader_get_builtin_block(GPUShader *shader, int builtin) { - return GPU_shaderinterface_block_builtin(shader->interface, - static_cast<GPUUniformBlockBuiltin>(builtin)); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + return interface->ubo_builtin((GPUUniformBlockBuiltin)builtin); } +/* DEPRECATED. */ int GPU_shader_get_uniform_block(GPUShader *shader, const char *name) { - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *ubo = interface->ubo_get(name); return ubo ? ubo->location : -1; } int GPU_shader_get_uniform_block_binding(GPUShader *shader, const char *name) { - const GPUShaderInput *ubo = GPU_shaderinterface_ubo(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *ubo = interface->ubo_get(name); return ubo ? ubo->binding : -1; } int GPU_shader_get_texture_binding(GPUShader *shader, const char *name) { - const GPUShaderInput *tex = GPU_shaderinterface_uniform(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *tex = interface->uniform_get(name); return tex ? tex->binding : -1; } int GPU_shader_get_attribute(GPUShader *shader, const char *name) { - const GPUShaderInput *attr = GPU_shaderinterface_attr(shader->interface, name); + ShaderInterface *interface = reinterpret_cast<Shader *>(shader)->interface; + const ShaderInput *attr = interface->attr_get(name); return attr ? attr->location : -1; } @@ -546,13 +555,13 @@ int GPU_shader_get_program(GPUShader *UNUSED(shader)) void GPU_shader_uniform_vector( GPUShader *shader, int loc, int len, int arraysize, const float *value) { - static_cast<Shader *>(shader)->uniform_float(loc, len, arraysize, value); + reinterpret_cast<Shader *>(shader)->uniform_float(loc, len, arraysize, value); } void GPU_shader_uniform_vector_int( GPUShader *shader, int loc, int len, int arraysize, const int *value) { - static_cast<Shader *>(shader)->uniform_int(loc, len, arraysize, value); + reinterpret_cast<Shader *>(shader)->uniform_int(loc, len, arraysize, value); } void GPU_shader_uniform_int(GPUShader *shader, int location, int value) @@ -565,14 +574,10 @@ void GPU_shader_uniform_float(GPUShader *shader, int location, float value) GPU_shader_uniform_vector(shader, location, 1, 1, &value); } -#define GET_UNIFORM \ - const GPUShaderInput *uniform = GPU_shaderinterface_uniform(sh->interface, name); \ - BLI_assert(uniform); - void GPU_shader_uniform_1i(GPUShader *sh, const char *name, int value) { - GET_UNIFORM - GPU_shader_uniform_int(sh, uniform->location, value); + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_int(sh, loc, value); } void GPU_shader_uniform_1b(GPUShader *sh, const char *name, bool value) @@ -600,44 +605,44 @@ void GPU_shader_uniform_4f(GPUShader *sh, const char *name, float x, float y, fl void GPU_shader_uniform_1f(GPUShader *sh, const char *name, float x) { - GET_UNIFORM - GPU_shader_uniform_float(sh, uniform->location, x); + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_float(sh, loc, x); } void GPU_shader_uniform_2fv(GPUShader *sh, const char *name, const float data[2]) { - GET_UNIFORM - GPU_shader_uniform_vector(sh, uniform->location, 2, 1, data); + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 2, 1, data); } void GPU_shader_uniform_3fv(GPUShader *sh, const char *name, const float data[3]) { - GET_UNIFORM - GPU_shader_uniform_vector(sh, uniform->location, 3, 1, data); + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 3, 1, data); } void GPU_shader_uniform_4fv(GPUShader *sh, const char *name, const float data[4]) { - GET_UNIFORM - GPU_shader_uniform_vector(sh, uniform->location, 4, 1, data); + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 4, 1, data); } void GPU_shader_uniform_mat4(GPUShader *sh, const char *name, const float data[4][4]) { - GET_UNIFORM - GPU_shader_uniform_vector(sh, uniform->location, 16, 1, (const float *)data); + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 16, 1, (const float *)data); } void GPU_shader_uniform_2fv_array(GPUShader *sh, const char *name, int len, const float (*val)[2]) { - GET_UNIFORM - GPU_shader_uniform_vector(sh, uniform->location, 2, len, (const float *)val); + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 2, len, (const float *)val); } void GPU_shader_uniform_4fv_array(GPUShader *sh, const char *name, int len, const float (*val)[4]) { - GET_UNIFORM - GPU_shader_uniform_vector(sh, uniform->location, 4, len, (const float *)val); + const int loc = GPU_shader_get_uniform(sh, name); + GPU_shader_uniform_vector(sh, loc, 4, len, (const float *)val); } /** \} */ @@ -657,7 +662,7 @@ static int g_shader_builtin_srgb_transform = 0; void GPU_shader_set_srgb_uniform(GPUShader *shader) { - int32_t loc = GPU_shaderinterface_uniform_builtin(shader->interface, GPU_UNIFORM_SRGB_TRANSFORM); + int32_t loc = GPU_shader_get_builtin_uniform(shader, GPU_UNIFORM_SRGB_TRANSFORM); if (loc != -1) { GPU_shader_uniform_vector_int(shader, loc, 1, 1, &g_shader_builtin_srgb_transform); } diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index da5bcaeca17..ed95a236da5 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -40,7 +40,7 @@ #include "GPU_platform.h" #include "GPU_shader.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" /* Adjust these constants as needed. */ #define MAX_DEFINE_LENGTH 256 diff --git a/source/blender/gpu/intern/gpu_shader_interface.cc b/source/blender/gpu/intern/gpu_shader_interface.cc index ef90dde1877..dc59dca9f78 100644 --- a/source/blender/gpu/intern/gpu_shader_interface.cc +++ b/source/blender/gpu/intern/gpu_shader_interface.cc @@ -23,161 +23,41 @@ * GPU shader interface (C --> GLSL) */ -#include "BKE_global.h" - -#include "BLI_bitmap.h" -#include "BLI_math_base.h" - #include "MEM_guardedalloc.h" -#include "GPU_shader_interface.h" - -#include "gpu_batch_private.hh" -#include "gpu_context_private.hh" - -#include "gl_batch.hh" - -#include <stddef.h> -#include <stdlib.h> -#include <string.h> - -#define DEBUG_SHADER_INTERFACE 0 - -#if DEBUG_SHADER_INTERFACE -# include <stdio.h> -#endif - -using namespace blender::gpu; - -static const char *BuiltinUniform_name(GPUUniformBuiltin u) -{ - switch (u) { - case GPU_UNIFORM_MODEL: - return "ModelMatrix"; - case GPU_UNIFORM_VIEW: - return "ViewMatrix"; - case GPU_UNIFORM_MODELVIEW: - return "ModelViewMatrix"; - case GPU_UNIFORM_PROJECTION: - return "ProjectionMatrix"; - case GPU_UNIFORM_VIEWPROJECTION: - return "ViewProjectionMatrix"; - case GPU_UNIFORM_MVP: - return "ModelViewProjectionMatrix"; - - case GPU_UNIFORM_MODEL_INV: - return "ModelMatrixInverse"; - case GPU_UNIFORM_VIEW_INV: - return "ViewMatrixInverse"; - case GPU_UNIFORM_MODELVIEW_INV: - return "ModelViewMatrixInverse"; - case GPU_UNIFORM_PROJECTION_INV: - return "ProjectionMatrixInverse"; - case GPU_UNIFORM_VIEWPROJECTION_INV: - return "ViewProjectionMatrixInverse"; - - case GPU_UNIFORM_NORMAL: - return "NormalMatrix"; - case GPU_UNIFORM_ORCO: - return "OrcoTexCoFactors"; - case GPU_UNIFORM_CLIPPLANES: - return "WorldClipPlanes"; - - case GPU_UNIFORM_COLOR: - return "color"; - case GPU_UNIFORM_BASE_INSTANCE: - return "baseInstance"; - case GPU_UNIFORM_RESOURCE_CHUNK: - return "resourceChunk"; - case GPU_UNIFORM_RESOURCE_ID: - return "resourceId"; - case GPU_UNIFORM_SRGB_TRANSFORM: - return "srgbTarget"; +#include "BLI_span.hh" +#include "BLI_vector.hh" - default: - return NULL; - } -} +#include "gpu_shader_interface.hh" -static const char *BuiltinUniformBlock_name(GPUUniformBlockBuiltin u) -{ - switch (u) { - case GPU_UNIFORM_BLOCK_VIEW: - return "viewBlock"; - case GPU_UNIFORM_BLOCK_MODEL: - return "modelBlock"; - case GPU_UNIFORM_BLOCK_INFO: - return "infoBlock"; - default: - return NULL; - } -} +namespace blender::gpu { -GPU_INLINE bool match(const char *a, const char *b) +ShaderInterface::ShaderInterface(void) { - return STREQ(a, b); + /* TODO(fclem) add unique ID for debugging. */ } -GPU_INLINE uint hash_string(const char *str) +ShaderInterface::~ShaderInterface(void) { - uint i = 0, c; - while ((c = *str++)) { - i = i * 37 + c; - } - return i; + /* Free memory used by name_buffer. */ + MEM_freeN(name_buffer_); + MEM_freeN(inputs_); } -GPU_INLINE uint32_t set_input_name(GPUShaderInterface *shaderface, - GPUShaderInput *input, - char *name, - uint32_t name_len) +static void sort_input_list(MutableSpan<ShaderInput> dst) { - /* remove "[0]" from array name */ - if (name[name_len - 1] == ']') { - name[name_len - 3] = '\0'; - name_len -= 3; + if (dst.size() == 0) { + return; } - input->name_offset = (uint32_t)(name - shaderface->name_buffer); - input->name_hash = hash_string(name); - return name_len + 1; /* include NULL terminator */ -} - -GPU_INLINE const GPUShaderInput *input_lookup(const GPUShaderInterface *shaderface, - const GPUShaderInput *const inputs, - const uint inputs_len, - const char *name) -{ - const uint name_hash = hash_string(name); - /* Simple linear search for now. */ - for (int i = inputs_len - 1; i >= 0; i--) { - if (inputs[i].name_hash == name_hash) { - if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) { - /* Hash colision resolve. */ - for (; i >= 0 && inputs[i].name_hash == name_hash; i--) { - if (match(name, shaderface->name_buffer + inputs[i].name_offset)) { - return inputs + i; /* not found */ - } - } - return NULL; /* not found */ - } - - /* This is a bit dangerous since we could have a hash collision. - * where the asked uniform that does not exist has the same hash - * as a real uniform. */ - BLI_assert(match(name, shaderface->name_buffer + inputs[i].name_offset)); - return inputs + i; - } - } - return NULL; /* not found */ -} + Vector<ShaderInput> inputs_vec = Vector<ShaderInput>(dst.size()); + MutableSpan<ShaderInput> src = inputs_vec.as_mutable_span(); + src.copy_from(dst); -/* Note that this modify the src array. */ -GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const uint input_len) -{ - for (uint i = 0; i < input_len; i++) { - GPUShaderInput *input_src = &src[0]; - for (uint j = 1; j < input_len; j++) { + /* Simple sorting by going through the array and selecting the biggest element each time. */ + for (uint i = 0; i < dst.size(); i++) { + ShaderInput *input_src = &src[0]; + for (uint j = 1; j < src.size(); j++) { if (src[j].name_hash > input_src->name_hash) { input_src = &src[j]; } @@ -187,360 +67,60 @@ GPU_INLINE void sort_input_list(GPUShaderInput *dst, GPUShaderInput *src, const } } -static int block_binding(int32_t program, uint32_t block_index) +/* Sorts all inputs inside their respective array. + * This is to allow fast hash collision detection. + * See ShaderInterface::input_lookup for more details. */ +void ShaderInterface::sort_inputs(void) { - /* For now just assign a consecutive index. In the future, we should set it in - * the shader using layout(binding = i) and query its value. */ - glUniformBlockBinding(program, block_index, block_index); - return block_index; + sort_input_list(MutableSpan<ShaderInput>(inputs_, attr_len_)); + sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_, ubo_len_)); + sort_input_list(MutableSpan<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_)); } -static int sampler_binding(int32_t program, - uint32_t uniform_index, - int32_t uniform_location, - int *sampler_len) +void ShaderInterface::debug_print(void) { - /* Identify sampler uniforms and asign sampler units to them. */ - GLint type; - glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type); - - switch (type) { - case GL_SAMPLER_1D: - case GL_SAMPLER_2D: - case GL_SAMPLER_3D: - case GL_SAMPLER_CUBE: - case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */ - case GL_SAMPLER_1D_SHADOW: - case GL_SAMPLER_2D_SHADOW: - case GL_SAMPLER_1D_ARRAY: - case GL_SAMPLER_2D_ARRAY: - case GL_SAMPLER_1D_ARRAY_SHADOW: - case GL_SAMPLER_2D_ARRAY_SHADOW: - case GL_SAMPLER_2D_MULTISAMPLE: - case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_SAMPLER_CUBE_SHADOW: - case GL_SAMPLER_BUFFER: - case GL_INT_SAMPLER_1D: - case GL_INT_SAMPLER_2D: - case GL_INT_SAMPLER_3D: - case GL_INT_SAMPLER_CUBE: - case GL_INT_SAMPLER_1D_ARRAY: - case GL_INT_SAMPLER_2D_ARRAY: - case GL_INT_SAMPLER_2D_MULTISAMPLE: - case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_INT_SAMPLER_BUFFER: - case GL_UNSIGNED_INT_SAMPLER_1D: - case GL_UNSIGNED_INT_SAMPLER_2D: - case GL_UNSIGNED_INT_SAMPLER_3D: - case GL_UNSIGNED_INT_SAMPLER_CUBE: - case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: - case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: - case GL_UNSIGNED_INT_SAMPLER_BUFFER: { - /* For now just assign a consecutive index. In the future, we should set it in - * the shader using layout(binding = i) and query its value. */ - int binding = *sampler_len; - glUniform1i(uniform_location, binding); - (*sampler_len)++; - return binding; - } - default: - return -1; - } -} + Span<ShaderInput> attrs = Span<ShaderInput>(inputs_, attr_len_); + Span<ShaderInput> ubos = Span<ShaderInput>(inputs_ + attr_len_, ubo_len_); + Span<ShaderInput> uniforms = Span<ShaderInput>(inputs_ + attr_len_ + ubo_len_, uniform_len_); + char *name_buf = name_buffer_; + const char format[] = " | %.8x : %4d : %s\n"; -GPUShaderInterface *GPU_shaderinterface_create(int32_t program) -{ -#ifndef NDEBUG - GLint curr_program; - glGetIntegerv(GL_CURRENT_PROGRAM, &curr_program); - BLI_assert(curr_program == program); -#endif - - GLint max_attr_name_len = 0, attr_len = 0; - glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len); - glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len); - - GLint max_ubo_name_len = 0, ubo_len = 0; - glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len); - glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len); - - GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0; - glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len); - glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); - uniform_len = active_uniform_len; - - /* Work around driver bug with Intel HD 4600 on Windows 7/8, where - * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */ - if (attr_len > 0 && max_attr_name_len == 0) { - max_attr_name_len = 256; - } - if (ubo_len > 0 && max_ubo_name_len == 0) { - max_ubo_name_len = 256; + printf(" \033[1mGPUShaderInterface : \033[0m\n"); + if (attrs.size() > 0) { + printf("\n Attributes :\n"); } - if (uniform_len > 0 && max_uniform_name_len == 0) { - max_uniform_name_len = 256; + for (const ShaderInput &attr : attrs) { + printf(format, attr.name_hash, attr.location, name_buf + attr.name_offset); } - /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before - * allocating the uniform array. */ - GLint max_ubo_uni_len = 0; - for (int i = 0; i < ubo_len; i++) { - GLint ubo_uni_len; - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); - max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len); - uniform_len -= ubo_uni_len; + if (uniforms.size() > 0) { + printf("\n Uniforms :\n"); } - /* Bit set to true if uniform comes from a uniform block. */ - BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__); - /* Set uniforms from block for exclusion. */ - GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); - for (int i = 0; i < ubo_len; i++) { - GLint ubo_uni_len; - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); - glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids); - for (int u = 0; u < ubo_uni_len; u++) { - BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]); + for (const ShaderInput &uni : uniforms) { + /* Bypass samplers. */ + if (uni.binding == -1) { + printf(format, uni.name_hash, uni.location, name_buf + uni.name_offset); } } - MEM_freeN(ubo_uni_ids); - uint32_t name_buffer_offset = 0; - const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + - uniform_len * max_uniform_name_len; - - int input_tot_len = attr_len + ubo_len + uniform_len; - size_t interface_size = sizeof(GPUShaderInterface) + sizeof(GPUShaderInput) * input_tot_len; - - GPUShaderInterface *shaderface = (GPUShaderInterface *)MEM_callocN(interface_size, - "GPUShaderInterface"); - shaderface->attribute_len = attr_len; - shaderface->ubo_len = ubo_len; - shaderface->uniform_len = uniform_len; - shaderface->name_buffer = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); - GPUShaderInput *inputs = shaderface->inputs; - - /* Temp buffer. */ - int input_tmp_len = max_iii(attr_len, ubo_len, uniform_len); - GPUShaderInput *inputs_tmp = (GPUShaderInput *)MEM_mallocN( - sizeof(GPUShaderInput) * input_tmp_len, "name_buffer"); - - /* Attributes */ - shaderface->enabled_attr_mask = 0; - for (int i = 0, idx = 0; i < attr_len; i++) { - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - GLenum type; - GLint size; - - glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name); - GLint location = glGetAttribLocation(program, name); - /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ - if (location == -1) { - shaderface->attribute_len--; - continue; - } - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->location = input->binding = location; - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_attr_mask |= (1 << input->location); + if (ubos.size() > 0) { + printf("\n Uniform Buffer Objects :\n"); } - sort_input_list(inputs, inputs_tmp, shaderface->attribute_len); - inputs += shaderface->attribute_len; - - /* Uniform Blocks */ - for (int i = 0, idx = 0; i < ubo_len; i++) { - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - - glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name); - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->binding = input->location = block_binding(program, i); - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_ubo_mask |= (1 << input->binding); + for (const ShaderInput &ubo : ubos) { + printf(format, ubo.name_hash, ubo.binding, name_buf + ubo.name_offset); } - sort_input_list(inputs, inputs_tmp, shaderface->ubo_len); - inputs += shaderface->ubo_len; - /* Uniforms */ - for (int i = 0, idx = 0, sampler = 0; i < active_uniform_len; i++) { - if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) { - continue; - } - char *name = shaderface->name_buffer + name_buffer_offset; - GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; - GLsizei name_len = 0; - - glGetActiveUniformName(program, i, remaining_buffer, &name_len, name); - - GPUShaderInput *input = &inputs_tmp[idx++]; - input->location = glGetUniformLocation(program, name); - input->binding = sampler_binding(program, i, input->location, &sampler); - - name_buffer_offset += set_input_name(shaderface, input, name, name_len); - shaderface->enabled_tex_mask |= (input->binding != -1) ? (1lu << input->binding) : 0lu; - } - sort_input_list(inputs, inputs_tmp, shaderface->uniform_len); - - /* Builtin Uniforms */ - for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { - GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); - shaderface->builtins[u] = glGetUniformLocation(program, BuiltinUniform_name(u)); - } - - /* Builtin Uniforms Blocks */ - for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) { - GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int); - const GPUShaderInput *block = GPU_shaderinterface_ubo(shaderface, BuiltinUniformBlock_name(u)); - shaderface->builtin_blocks[u] = (block != NULL) ? block->binding : -1; - } - - /* Batches ref buffer */ - shaderface->batches_len = GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = (void **)MEM_callocN(shaderface->batches_len * sizeof(GPUBatch *), - "GPUShaderInterface batches"); - - MEM_freeN(uniforms_from_blocks); - MEM_freeN(inputs_tmp); - - /* Resize name buffer to save some memory. */ - if (name_buffer_offset < name_buffer_len) { - shaderface->name_buffer = (char *)MEM_reallocN(shaderface->name_buffer, name_buffer_offset); - } - -#if DEBUG_SHADER_INTERFACE - char *name_buf = shaderface->name_buffer; - printf("--- GPUShaderInterface %p, program %d ---\n", shaderface, program); - if (shaderface->attribute_len > 0) { - printf("Attributes {\n"); - for (int i = 0; i < shaderface->attribute_len; i++) { - GPUShaderInput *input = shaderface->inputs + i; - printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset); - } - printf("};\n"); - } - if (shaderface->ubo_len > 0) { - printf("Uniform Buffer Objects {\n"); - for (int i = 0; i < shaderface->ubo_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + i; - printf("\t(binding = %d) %s;\n", input->binding, name_buf + input->name_offset); - } - printf("};\n"); + if (enabled_tex_mask_ > 0) { + printf("\n Samplers :\n"); } - if (shaderface->enabled_tex_mask > 0) { - printf("Samplers {\n"); - for (int i = 0; i < shaderface->uniform_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + - shaderface->ubo_len + i; - if (input->binding != -1) { - printf("\t(location = %d, binding = %d) %s;\n", - input->location, - input->binding, - name_buf + input->name_offset); - } - } - printf("};\n"); - } - if (shaderface->uniform_len > 0) { - printf("Uniforms {\n"); - for (int i = 0; i < shaderface->uniform_len; i++) { - GPUShaderInput *input = shaderface->inputs + shaderface->attribute_len + - shaderface->ubo_len + i; - if (input->binding == -1) { - printf("\t(location = %d) %s;\n", input->location, name_buf + input->name_offset); - } + for (const ShaderInput &samp : uniforms) { + /* Bypass uniforms. */ + if (samp.binding != -1) { + printf(format, samp.name_hash, samp.binding, name_buf + samp.name_offset); } - printf("};\n"); } - printf("--- GPUShaderInterface end ---\n\n"); -#endif - return shaderface; -} - -void GPU_shaderinterface_discard(GPUShaderInterface *shaderface) -{ - /* Free memory used by name_buffer. */ - MEM_freeN(shaderface->name_buffer); - /* Remove this interface from all linked Batches vao cache. */ - for (int i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] != NULL) { - /* XXX GL specific. to be removed during refactor. */ - reinterpret_cast<GLVaoCache *>(shaderface->batches[i])->remove(shaderface); - } - } - MEM_freeN(shaderface->batches); - /* Free memory used by shader interface by its self. */ - MEM_freeN(shaderface); + printf("\n"); } -const GPUShaderInput *GPU_shaderinterface_attr(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = 0; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->attribute_len, name); -} - -const GPUShaderInput *GPU_shaderinterface_ubo(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = shaderface->attribute_len; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->ubo_len, name); -} - -const GPUShaderInput *GPU_shaderinterface_uniform(const GPUShaderInterface *shaderface, - const char *name) -{ - uint ofs = shaderface->attribute_len + shaderface->ubo_len; - return input_lookup(shaderface, shaderface->inputs + ofs, shaderface->uniform_len, name); -} - -int32_t GPU_shaderinterface_uniform_builtin(const GPUShaderInterface *shaderface, - GPUUniformBuiltin builtin) -{ - BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS); - return shaderface->builtins[builtin]; -} - -int32_t GPU_shaderinterface_block_builtin(const GPUShaderInterface *shaderface, - GPUUniformBlockBuiltin builtin) -{ - BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS); - return shaderface->builtin_blocks[builtin]; -} - -void GPU_shaderinterface_add_batch_ref(GPUShaderInterface *shaderface, void *batch) -{ - int i; /* find first unused slot */ - for (i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] == NULL) { - break; - } - } - if (i == shaderface->batches_len) { - /* Not enough place, realloc the array. */ - i = shaderface->batches_len; - shaderface->batches_len += GPU_SHADERINTERFACE_REF_ALLOC_COUNT; - shaderface->batches = (void **)MEM_recallocN(shaderface->batches, - sizeof(void *) * shaderface->batches_len); - } - /** XXX todo cleanup. */ - shaderface->batches[i] = reinterpret_cast<void *>(batch); -} - -void GPU_shaderinterface_remove_batch_ref(GPUShaderInterface *shaderface, void *batch) -{ - for (int i = 0; i < shaderface->batches_len; i++) { - if (shaderface->batches[i] == batch) { - shaderface->batches[i] = NULL; - break; /* cannot have duplicates */ - } - } -} +} // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_shader_interface.hh b/source/blender/gpu/intern/gpu_shader_interface.hh new file mode 100644 index 00000000000..6e1cb342c68 --- /dev/null +++ b/source/blender/gpu/intern/gpu_shader_interface.hh @@ -0,0 +1,229 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + * + * Structure detailing needed vertex inputs and resources for a specific shader. + * A shader interface can be shared between two similar shaders. + */ + +#pragma once + +#include <cstring> /* required for STREQ later on. */ + +#include "BLI_hash.h" +#include "BLI_utildefines.h" + +#include "GPU_shader.h" + +namespace blender::gpu { + +typedef struct ShaderInput { + uint32_t name_offset; + uint32_t name_hash; + int32_t location; + /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */ + int32_t binding; +} ShaderInput; + +/** + * Implementation of Shader interface. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class ShaderInterface { + /* TODO(fclem) should be protected. */ + public: + /** Flat array. In this order: Attributes, Ubos, Uniforms. */ + ShaderInput *inputs_ = NULL; + /** Buffer containing all inputs names separated by '\0'. */ + char *name_buffer_ = NULL; + /** Input counts inside input array. */ + uint attr_len_ = 0; + uint ubo_len_ = 0; + uint uniform_len_ = 0; + /** Enabled bindpoints that needs to be fed with data. */ + uint16_t enabled_attr_mask_ = 0; + uint16_t enabled_ubo_mask_ = 0; + uint64_t enabled_tex_mask_ = 0; + /** Location of builtin uniforms. Fast access, no lookup needed. */ + int32_t builtins_[GPU_NUM_UNIFORMS]; + int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS]; + + public: + ShaderInterface(); + virtual ~ShaderInterface(); + + void debug_print(void); + + inline const ShaderInput *attr_get(const char *name) const + { + return input_lookup(inputs_, attr_len_, name); + } + + inline const ShaderInput *ubo_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_, ubo_len_, name); + } + + inline const ShaderInput *uniform_get(const char *name) const + { + return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, name); + } + + /* Returns uniform location. */ + inline int32_t uniform_builtin(const GPUUniformBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS); + return builtins_[builtin]; + } + + /* Returns binding position. */ + inline int32_t ubo_builtin(const GPUUniformBlockBuiltin builtin) const + { + BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS); + return builtin_blocks_[builtin]; + } + + protected: + static inline const char *builtin_uniform_name(GPUUniformBuiltin u); + static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u); + + inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const; + + /* Finalize interface construction by sorting the ShaderInputs for faster lookups. */ + void sort_inputs(void); + + private: + inline const ShaderInput *input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const char *name) const; +}; + +inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u) +{ + switch (u) { + case GPU_UNIFORM_MODEL: + return "ModelMatrix"; + case GPU_UNIFORM_VIEW: + return "ViewMatrix"; + case GPU_UNIFORM_MODELVIEW: + return "ModelViewMatrix"; + case GPU_UNIFORM_PROJECTION: + return "ProjectionMatrix"; + case GPU_UNIFORM_VIEWPROJECTION: + return "ViewProjectionMatrix"; + case GPU_UNIFORM_MVP: + return "ModelViewProjectionMatrix"; + + case GPU_UNIFORM_MODEL_INV: + return "ModelMatrixInverse"; + case GPU_UNIFORM_VIEW_INV: + return "ViewMatrixInverse"; + case GPU_UNIFORM_MODELVIEW_INV: + return "ModelViewMatrixInverse"; + case GPU_UNIFORM_PROJECTION_INV: + return "ProjectionMatrixInverse"; + case GPU_UNIFORM_VIEWPROJECTION_INV: + return "ViewProjectionMatrixInverse"; + + case GPU_UNIFORM_NORMAL: + return "NormalMatrix"; + case GPU_UNIFORM_ORCO: + return "OrcoTexCoFactors"; + case GPU_UNIFORM_CLIPPLANES: + return "WorldClipPlanes"; + + case GPU_UNIFORM_COLOR: + return "color"; + case GPU_UNIFORM_BASE_INSTANCE: + return "baseInstance"; + case GPU_UNIFORM_RESOURCE_CHUNK: + return "resourceChunk"; + case GPU_UNIFORM_RESOURCE_ID: + return "resourceId"; + case GPU_UNIFORM_SRGB_TRANSFORM: + return "srgbTarget"; + + default: + return NULL; + } +} + +inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBuiltin u) +{ + switch (u) { + case GPU_UNIFORM_BLOCK_VIEW: + return "viewBlock"; + case GPU_UNIFORM_BLOCK_MODEL: + return "modelBlock"; + case GPU_UNIFORM_BLOCK_INFO: + return "infoBlock"; + default: + return NULL; + } +} + +/* Returns string length including '\0' terminator. */ +inline uint32_t ShaderInterface::set_input_name(ShaderInput *input, + char *name, + uint32_t name_len) const +{ + /* remove "[0]" from array name */ + if (name[name_len - 1] == ']') { + name[name_len - 3] = '\0'; + name_len -= 3; + } + + input->name_offset = (uint32_t)(name - name_buffer_); + input->name_hash = BLI_hash_string(name); + return name_len + 1; /* include NULL terminator */ +} + +inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs, + const uint inputs_len, + const char *name) const +{ + const uint name_hash = BLI_hash_string(name); + /* Simple linear search for now. */ + for (int i = inputs_len - 1; i >= 0; i--) { + if (inputs[i].name_hash == name_hash) { + if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) { + /* Hash colision resolve. */ + for (; i >= 0 && inputs[i].name_hash == name_hash; i--) { + if (STREQ(name, name_buffer_ + inputs[i].name_offset)) { + return inputs + i; /* not found */ + } + } + return NULL; /* not found */ + } + + /* This is a bit dangerous since we could have a hash collision. + * where the asked uniform that does not exist has the same hash + * as a real uniform. */ + BLI_assert(STREQ(name, name_buffer_ + inputs[i].name_offset)); + return inputs + i; + } + } + return NULL; /* not found */ +} + +} // namespace blender::gpu diff --git a/source/blender/gpu/intern/gpu_shader_private.hh b/source/blender/gpu/intern/gpu_shader_private.hh index 1f667fb4cf9..d56a7b2500b 100644 --- a/source/blender/gpu/intern/gpu_shader_private.hh +++ b/source/blender/gpu/intern/gpu_shader_private.hh @@ -23,13 +23,25 @@ #include "BLI_span.hh" #include "GPU_shader.h" -#include "GPU_shader_interface.h" #include "GPU_vertex_buffer.h" +#include "gpu_shader_interface.hh" namespace blender { namespace gpu { -class Shader : public GPUShader { +/** + * Implementation of shader compilation and uniforms handling. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class Shader { + public: + /** Uniform & attribute locations for shader. */ + ShaderInterface *interface = nullptr; + + protected: + /** For debugging purpose. */ + char name[64]; + public: Shader(const char *name); virtual ~Shader(); diff --git a/source/blender/gpu/intern/gpu_state.cc b/source/blender/gpu/intern/gpu_state.cc index f02ec9c5cd4..fcbaa500e2d 100644 --- a/source/blender/gpu/intern/gpu_state.cc +++ b/source/blender/gpu/intern/gpu_state.cc @@ -44,7 +44,7 @@ using namespace blender::gpu; do { \ GPUStateManager *stack = GPU_context_active_get()->state_manager; \ auto &state_object = stack->_prefix##state; \ - state_object._state = _value; \ + state_object._state = (_value); \ } while (0) #define SET_IMMUTABLE_STATE(_state, _value) SET_STATE(, _state, _value) @@ -74,10 +74,14 @@ void GPU_provoking_vertex(eGPUProvokingVertex vert) SET_IMMUTABLE_STATE(provoking_vert, vert); } -/* TODO explicit depth test. */ -void GPU_depth_test(bool enable) +void GPU_depth_test(eGPUDepthTest test) { - SET_IMMUTABLE_STATE(depth_test, (enable) ? GPU_DEPTH_LESS_EQUAL : GPU_DEPTH_NONE); + SET_IMMUTABLE_STATE(depth_test, test); +} + +void GPU_stencil_test(eGPUStencilTest test) +{ + SET_IMMUTABLE_STATE(stencil_test, test); } void GPU_line_smooth(bool enable) @@ -104,11 +108,11 @@ void GPU_color_mask(bool r, bool g, bool b, bool a) { GPUStateManager *stack = GPU_context_active_get()->state_manager; auto &state = stack->state; - eGPUWriteMask write_mask = state.write_mask; - SET_FLAG_FROM_TEST(write_mask, r, GPU_WRITE_RED); - SET_FLAG_FROM_TEST(write_mask, g, GPU_WRITE_GREEN); - SET_FLAG_FROM_TEST(write_mask, b, GPU_WRITE_BLUE); - SET_FLAG_FROM_TEST(write_mask, a, GPU_WRITE_ALPHA); + uint32_t write_mask = state.write_mask; + SET_FLAG_FROM_TEST(write_mask, r, (uint32_t)GPU_WRITE_RED); + SET_FLAG_FROM_TEST(write_mask, g, (uint32_t)GPU_WRITE_GREEN); + SET_FLAG_FROM_TEST(write_mask, b, (uint32_t)GPU_WRITE_BLUE); + SET_FLAG_FROM_TEST(write_mask, a, (uint32_t)GPU_WRITE_ALPHA); state.write_mask = write_mask; } @@ -116,8 +120,8 @@ void GPU_depth_mask(bool depth) { GPUStateManager *stack = GPU_context_active_get()->state_manager; auto &state = stack->state; - eGPUWriteMask write_mask = state.write_mask; - SET_FLAG_FROM_TEST(write_mask, depth, GPU_WRITE_DEPTH); + uint32_t write_mask = state.write_mask; + SET_FLAG_FROM_TEST(write_mask, depth, (uint32_t)GPU_WRITE_DEPTH); state.write_mask = write_mask; } @@ -141,13 +145,13 @@ void GPU_state_set(eGPUWriteMask write_mask, { GPUStateManager *stack = GPU_context_active_get()->state_manager; auto &state = stack->state; - state.write_mask = write_mask; - state.blend = blend; - state.culling_test = culling_test; - state.depth_test = depth_test; - state.stencil_test = stencil_test; - state.stencil_op = stencil_op; - state.provoking_vert = provoking_vert; + state.write_mask = (uint32_t)write_mask; + state.blend = (uint32_t)blend; + state.culling_test = (uint32_t)culling_test; + state.depth_test = (uint32_t)depth_test; + state.stencil_test = (uint32_t)stencil_test; + state.stencil_op = (uint32_t)stencil_op; + state.provoking_vert = (uint32_t)provoking_vert; } /** \} */ @@ -197,7 +201,8 @@ void GPU_scissor(int x, int y, int width, int height) { GPUStateManager *stack = GPU_context_active_get()->state_manager; auto &state = stack->mutable_state; - int scissor_rect[4] = {x, y, width, height}; + bool enabled = state.scissor_rect[2] > 0; + int scissor_rect[4] = {x, y, enabled ? width : -width, height}; copy_v4_v4_int(state.scissor_rect, scissor_rect); } @@ -213,10 +218,12 @@ void GPU_stencil_reference_set(uint reference) { SET_MUTABLE_STATE(stencil_reference, (uint8_t)reference); } + void GPU_stencil_write_mask_set(uint write_mask) { SET_MUTABLE_STATE(stencil_write_mask, (uint8_t)write_mask); } + void GPU_stencil_compare_mask_set(uint compare_mask) { SET_MUTABLE_STATE(stencil_compare_mask, (uint8_t)compare_mask); @@ -231,19 +238,31 @@ void GPU_stencil_compare_mask_set(uint compare_mask) eGPUBlend GPU_blend_get() { GPUState &state = GPU_context_active_get()->state_manager->state; - return state.blend; + return (eGPUBlend)state.blend; } eGPUWriteMask GPU_write_mask_get() { GPUState &state = GPU_context_active_get()->state_manager->state; - return state.write_mask; + return (eGPUWriteMask)state.write_mask; +} + +uint GPU_stencil_mask_get() +{ + GPUStateMutable &state = GPU_context_active_get()->state_manager->mutable_state; + return state.stencil_write_mask; +} + +eGPUDepthTest GPU_depth_test_get() +{ + GPUState &state = GPU_context_active_get()->state_manager->state; + return (eGPUDepthTest)state.depth_test; } -bool GPU_depth_test_enabled() +eGPUStencilTest GPU_stencil_test_get() { GPUState &state = GPU_context_active_get()->state_manager->state; - return state.depth_test != GPU_DEPTH_NONE; + return (eGPUStencilTest)state.stencil_test; } void GPU_scissor_get(int coords[4]) diff --git a/source/blender/gpu/intern/gpu_state_private.hh b/source/blender/gpu/intern/gpu_state_private.hh index 1ba79c7c048..a1bfefbaff5 100644 --- a/source/blender/gpu/intern/gpu_state_private.hh +++ b/source/blender/gpu/intern/gpu_state_private.hh @@ -35,13 +35,20 @@ namespace gpu { * Try to keep small to reduce validation time. */ union GPUState { struct { - eGPUWriteMask write_mask : 13; - eGPUBlend blend : 4; - eGPUFaceCullTest culling_test : 2; - eGPUDepthTest depth_test : 3; - eGPUStencilTest stencil_test : 3; - eGPUStencilOp stencil_op : 3; - eGPUProvokingVertex provoking_vert : 1; + /** eGPUWriteMask */ + uint32_t write_mask : 13; + /** eGPUBlend */ + uint32_t blend : 4; + /** eGPUFaceCullTest */ + uint32_t culling_test : 2; + /** eGPUDepthTest */ + uint32_t depth_test : 3; + /** eGPUStencilTest */ + uint32_t stencil_test : 3; + /** eGPUStencilOp */ + uint32_t stencil_op : 3; + /** eGPUProvokingVertex */ + uint32_t provoking_vert : 1; /** Enable bits. */ uint32_t logic_op_xor : 1; uint32_t invert_facing : 1; @@ -144,6 +151,10 @@ inline GPUStateMutable operator~(const GPUStateMutable &a) return r; } +/** + * State manager keeping track of the draw state and applying it before drawing. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ class GPUStateManager { public: GPUState state; diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index 91990dac83f..e70eeece104 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -1158,9 +1158,9 @@ static GLenum convert_target_to_gl(int dimension, bool is_array) { switch (dimension) { case 1: - return is_array ? GL_TEXTURE_1D : GL_TEXTURE_1D_ARRAY; + return is_array ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D; case 2: - return is_array ? GL_TEXTURE_2D : GL_TEXTURE_2D_ARRAY; + return is_array ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D; case 3: return GL_TEXTURE_3D; default: @@ -2179,6 +2179,11 @@ void GPU_texture_get_mipmap_size(GPUTexture *tex, int lvl, int *size) void GPU_samplers_init(void) { + float max_anisotropy = 1.0f; + if (GLEW_EXT_texture_filter_anisotropic) { + glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_anisotropy); + } + glGenSamplers(GPU_SAMPLER_MAX, GG.samplers); for (int i = 0; i < GPU_SAMPLER_MAX; i++) { eGPUSamplerState state = static_cast<eGPUSamplerState>(i); @@ -2193,7 +2198,7 @@ void GPU_samplers_init(void) GLenum compare_mode = (state & GPU_SAMPLER_COMPARE) ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE; /* TODO(fclem) Anisotropic level should be a render engine parameter. */ float aniso_filter = ((state & GPU_SAMPLER_MIPMAP) && (state & GPU_SAMPLER_ANISO)) ? - U.anisotropic_filter : + max_ff(max_anisotropy, U.anisotropic_filter) : 1.0f; glSamplerParameteri(GG.samplers[i], GL_TEXTURE_WRAP_S, wrap_s); diff --git a/source/blender/gpu/intern/gpu_uniformbuffer.cc b/source/blender/gpu/intern/gpu_uniform_buffer.cc index e203ffd848f..94aa6bd76ab 100644 --- a/source/blender/gpu/intern/gpu_uniformbuffer.cc +++ b/source/blender/gpu/intern/gpu_uniform_buffer.cc @@ -13,7 +13,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * The Original Code is Copyright (C) 2005 Blender Foundation. + * The Original Code is Copyright (C) 2020 Blender Foundation. * All rights reserved. */ @@ -27,58 +27,46 @@ #include "BLI_blenlib.h" #include "BLI_math_base.h" -#include "gpu_context_private.hh" +#include "gpu_backend.hh" #include "gpu_node_graph.h" -#include "GPU_extensions.h" -#include "GPU_glew.h" #include "GPU_material.h" -#include "GPU_uniformbuffer.h" - -typedef struct GPUUniformBuffer { - /** Data size in bytes. */ - int size; - /** GL handle for UBO. */ - GLuint bindcode; - /** Current binding point. */ - int bindpoint; - /** Continuous memory block to copy to GPU. Is own by the GPUUniformBuffer. */ - void *data; -} GPUUniformBuffer; - -GPUUniformBuffer *GPU_uniformbuffer_create(int size, const void *data, char err_out[256]) + +#include "GPU_extensions.h" + +#include "GPU_uniform_buffer.h" +#include "gpu_uniform_buffer_private.hh" + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +namespace blender::gpu { + +UniformBuf::UniformBuf(size_t size, const char *name) { /* Make sure that UBO is padded to size of vec4 */ BLI_assert((size % 16) == 0); + BLI_assert(size <= GPU_max_ubo_size()); - if (size > GPU_max_ubo_size()) { - if (err_out) { - BLI_strncpy(err_out, "GPUUniformBuffer: UBO too big", 256); - } - return NULL; - } - - GPUUniformBuffer *ubo = (GPUUniformBuffer *)MEM_mallocN(sizeof(GPUUniformBuffer), __func__); - ubo->size = size; - ubo->data = NULL; - ubo->bindcode = 0; - ubo->bindpoint = -1; - - /* Direct init. */ - if (data != NULL) { - GPU_uniformbuffer_update(ubo, data); - } + size_in_bytes_ = size; - return ubo; + BLI_strncpy(name_, name, sizeof(name_)); } -void GPU_uniformbuffer_free(GPUUniformBuffer *ubo) +UniformBuf::~UniformBuf() { - MEM_SAFE_FREE(ubo->data); - GPU_buf_free(ubo->bindcode); - MEM_freeN(ubo); + MEM_SAFE_FREE(data_); } +} // namespace blender::gpu + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Uniform buffer from GPUInput list + * \{ */ + /** * We need to pad some data types (vec3) on the C side * To match the GPU expected memory block alignment. @@ -111,10 +99,10 @@ static int inputs_cmp(const void *a, const void *b) * Make sure we respect the expected alignment of UBOs. * mat4, vec4, pad vec3 as vec4, then vec2, then floats. */ -static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) +static void buffer_from_list_inputs_sort(ListBase *inputs) { -/* Only support up to this type, if you want to extend it, make sure the - * padding logic is correct for the new types. */ +/* Only support up to this type, if you want to extend it, make sure static void + * inputs_sobuffer_size_compute *inputs) padding logic is correct for the new types. */ #define MAX_UBO_GPU_TYPE GPU_MAT4 /* Order them as mat4, vec4, vec3, vec2, float. */ @@ -173,23 +161,9 @@ static void gpu_uniformbuffer_inputs_sort(ListBase *inputs) #undef MAX_UBO_GPU_TYPE } -/** - * Create dynamic UBO from parameters - * Return NULL if failed to create or if \param inputs: is empty. - * - * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). - */ -GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_out[256]) +static inline size_t buffer_size_from_list(ListBase *inputs) { - /* There is no point on creating an UBO if there is no arguments. */ - if (BLI_listbase_is_empty(inputs)) { - return NULL; - } - /* Make sure we comply to the ubo alignment requirements. */ - gpu_uniformbuffer_inputs_sort(inputs); - size_t buffer_size = 0; - LISTBASE_FOREACH (LinkData *, link, inputs) { const eGPUType gputype = get_padded_gpu_type(link); buffer_size += gputype * sizeof(float); @@ -197,8 +171,12 @@ GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_ou /* Round up to size of vec4. (Opengl Requirement) */ size_t alignment = sizeof(float[4]); buffer_size = divide_ceil_u(buffer_size, alignment) * alignment; - void *data = MEM_mallocN(buffer_size, __func__); + return buffer_size; +} + +static inline void buffer_fill_from_list(void *data, ListBase *inputs) +{ /* Now that we know the total ubo size we can start populating it. */ float *offset = (float *)data; LISTBASE_FOREACH (LinkData *, link, inputs) { @@ -206,71 +184,73 @@ GPUUniformBuffer *GPU_uniformbuffer_dynamic_create(ListBase *inputs, char err_ou memcpy(offset, input->vec, input->type * sizeof(float)); offset += get_padded_gpu_type(link); } - - /* Pass data as NULL for late init. */ - GPUUniformBuffer *ubo = GPU_uniformbuffer_create(buffer_size, NULL, err_out); - /* Data will be update just before binding. */ - ubo->data = data; - return ubo; } -static void gpu_uniformbuffer_init(GPUUniformBuffer *ubo) -{ - BLI_assert(ubo->bindcode == 0); - ubo->bindcode = GPU_buf_alloc(); +/** \} */ - if (ubo->bindcode == 0) { - fprintf(stderr, "GPUUniformBuffer: UBO create failed"); - BLI_assert(0); - return; - } +/* -------------------------------------------------------------------- */ +/** \name C-API + * \{ */ - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferData(GL_UNIFORM_BUFFER, ubo->size, NULL, GL_DYNAMIC_DRAW); -} +using namespace blender::gpu; -void GPU_uniformbuffer_update(GPUUniformBuffer *ubo, const void *data) +GPUUniformBuf *GPU_uniformbuf_create_ex(size_t size, const void *data, const char *name) { - if (ubo->bindcode == 0) { - gpu_uniformbuffer_init(ubo); + UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(size, name); + /* Direct init. */ + if (data != NULL) { + ubo->update(data); } - - glBindBuffer(GL_UNIFORM_BUFFER, ubo->bindcode); - glBufferSubData(GL_UNIFORM_BUFFER, 0, ubo->size, data); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + return reinterpret_cast<GPUUniformBuf *>(ubo); } -void GPU_uniformbuffer_bind(GPUUniformBuffer *ubo, int number) +/** + * Create UBO from inputs list. + * Return NULL if failed to create or if \param inputs: is empty. + * + * \param inputs: ListBase of #BLI_genericNodeN(#GPUInput). + */ +GPUUniformBuf *GPU_uniformbuf_create_from_list(ListBase *inputs, const char *name) { - if (number >= GPU_max_ubo_binds()) { - fprintf(stderr, "Not enough UBO slots.\n"); - return; + /* There is no point on creating an UBO if there is no arguments. */ + if (BLI_listbase_is_empty(inputs)) { + return NULL; } - if (ubo->bindcode == 0) { - gpu_uniformbuffer_init(ubo); - } + buffer_from_list_inputs_sort(inputs); + size_t buffer_size = buffer_size_from_list(inputs); + void *data = MEM_mallocN(buffer_size, __func__); + buffer_fill_from_list(data, inputs); - if (ubo->data != NULL) { - GPU_uniformbuffer_update(ubo, ubo->data); - MEM_SAFE_FREE(ubo->data); - } + UniformBuf *ubo = GPUBackend::get()->uniformbuf_alloc(buffer_size, name); + /* Defer data upload. */ + ubo->attach_data(data); + return reinterpret_cast<GPUUniformBuf *>(ubo); +} - glBindBufferBase(GL_UNIFORM_BUFFER, number, ubo->bindcode); - ubo->bindpoint = number; +void GPU_uniformbuf_free(GPUUniformBuf *ubo) +{ + delete reinterpret_cast<UniformBuf *>(ubo); } -void GPU_uniformbuffer_unbind(GPUUniformBuffer *ubo) +void GPU_uniformbuf_update(GPUUniformBuf *ubo, const void *data) { -#ifndef NDEBUG - glBindBufferBase(GL_UNIFORM_BUFFER, ubo->bindpoint, 0); -#endif - ubo->bindpoint = 0; + reinterpret_cast<UniformBuf *>(ubo)->update(data); } -void GPU_uniformbuffer_unbind_all(void) +void GPU_uniformbuf_bind(GPUUniformBuf *ubo, int slot) { - for (int i = 0; i < GPU_max_ubo_binds(); i++) { - glBindBufferBase(GL_UNIFORM_BUFFER, i, 0); - } + reinterpret_cast<UniformBuf *>(ubo)->bind(slot); } + +void GPU_uniformbuf_unbind(GPUUniformBuf *ubo) +{ + reinterpret_cast<UniformBuf *>(ubo)->unbind(); +} + +void GPU_uniformbuf_unbind_all(void) +{ + /* FIXME */ +} + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_uniform_buffer_private.hh b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh new file mode 100644 index 00000000000..288cbae526e --- /dev/null +++ b/source/blender/gpu/intern/gpu_uniform_buffer_private.hh @@ -0,0 +1,69 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright 2020, Blender Foundation. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "BLI_sys_types.h" + +namespace blender { +namespace gpu { + +#ifdef DEBUG +# define DEBUG_NAME_LEN 8 +#else +# define DEBUG_NAME_LEN 64 +#endif + +/** + * Implementation of Uniform Buffers. + * Base class which is then specialized for each implementation (GL, VK, ...). + **/ +class UniformBuf { + protected: + /** Data size in bytes. */ + size_t size_in_bytes_; + /** Continuous memory block to copy to GPU. This data is owned by the UniformBuf. */ + void *data_ = NULL; + /** Debugging name */ + char name_[DEBUG_NAME_LEN]; + + public: + UniformBuf(size_t size, const char *name); + virtual ~UniformBuf(); + + virtual void update(const void *data) = 0; + virtual void bind(int slot) = 0; + virtual void unbind(void) = 0; + + /** Used to defer data upload at drawing time. + * This is useful if the thread has no context bound. + * This transfers ownership to this UniformBuf. */ + void attach_data(void *data) + { + data_ = data; + } +}; + +#undef DEBUG_NAME_LEN + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/intern/gpu_vertex_format.cc b/source/blender/gpu/intern/gpu_vertex_format.cc index 2b16f4482b4..ed317b3a0df 100644 --- a/source/blender/gpu/intern/gpu_vertex_format.cc +++ b/source/blender/gpu/intern/gpu_vertex_format.cc @@ -407,6 +407,6 @@ void VertexFormat_pack(GPUVertFormat *format) void GPU_vertformat_from_shader(GPUVertFormat *format, const struct GPUShader *gpushader) { - const Shader *shader = static_cast<const Shader *>(gpushader); + const Shader *shader = reinterpret_cast<const Shader *>(gpushader); shader->vertformat_from_shader(format); } diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index ba938349761..fd1265dc2a6 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -41,7 +41,7 @@ #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #include "GPU_viewport.h" #include "DRW_engine.h" @@ -1020,8 +1020,8 @@ void GPU_viewport_free(GPUViewport *viewport) } for (int i = 0; i < viewport->vmempool.ubo_len; i++) { - GPU_uniformbuffer_free(viewport->vmempool.matrices_ubo[i]); - GPU_uniformbuffer_free(viewport->vmempool.obinfos_ubo[i]); + GPU_uniformbuf_free(viewport->vmempool.matrices_ubo[i]); + GPU_uniformbuf_free(viewport->vmempool.obinfos_ubo[i]); } MEM_SAFE_FREE(viewport->vmempool.matrices_ubo); MEM_SAFE_FREE(viewport->vmempool.obinfos_ubo); diff --git a/source/blender/gpu/opengl/gl_backend.hh b/source/blender/gpu/opengl/gl_backend.hh index 63a14af6612..0234c1a3f63 100644 --- a/source/blender/gpu/opengl/gl_backend.hh +++ b/source/blender/gpu/opengl/gl_backend.hh @@ -31,6 +31,7 @@ #include "gl_context.hh" #include "gl_drawlist.hh" #include "gl_shader.hh" +#include "gl_uniform_buffer.hh" namespace blender { namespace gpu { @@ -40,6 +41,11 @@ class GLBackend : public GPUBackend { GLSharedOrphanLists shared_orphan_list_; public: + static GLBackend *get(void) + { + return static_cast<GLBackend *>(GPUBackend::get()); + } + GPUContext *context_alloc(void *ghost_window) { return new GLContext(ghost_window, shared_orphan_list_); @@ -60,6 +66,11 @@ class GLBackend : public GPUBackend { return new GLShader(name); }; + UniformBuf *uniformbuf_alloc(int size, const char *name) + { + return new GLUniformBuf(size, name); + }; + /* TODO remove */ void buf_free(GLuint buf_id); void tex_free(GLuint tex_id); diff --git a/source/blender/gpu/opengl/gl_batch.cc b/source/blender/gpu/opengl/gl_batch.cc index fade8763065..7d6270bca93 100644 --- a/source/blender/gpu/opengl/gl_batch.cc +++ b/source/blender/gpu/opengl/gl_batch.cc @@ -33,6 +33,7 @@ #include "gpu_batch_private.hh" #include "gpu_primitive_private.h" +#include "gpu_shader_private.hh" #include "gl_batch.hh" #include "gl_context.hh" @@ -68,10 +69,11 @@ void GLVaoCache::init(void) } vao_base_instance_ = 0; base_instance_ = 0; + vao_id_ = 0; } /* Create a new VAO object and store it in the cache. */ -void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) +void GLVaoCache::insert(const GLShaderInterface *interface, GLuint vao) { /* Now insert the cache. */ if (!is_dynamic_vao_count) { @@ -90,8 +92,7 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) /* Erase previous entries, they will be added back if drawn again. */ for (int i = 0; i < GPU_VAO_STATIC_LEN; i++) { if (static_vaos.interfaces[i] != NULL) { - GPU_shaderinterface_remove_batch_ref( - const_cast<GPUShaderInterface *>(static_vaos.interfaces[i]), this); + const_cast<GLShaderInterface *>(static_vaos.interfaces[i])->ref_remove(this); context_->vao_free(static_vaos.vao_ids[i]); } } @@ -99,8 +100,8 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) is_dynamic_vao_count = true; /* Init dynamic arrays and let the branch below set the values. */ dynamic_vaos.count = GPU_BATCH_VAO_DYN_ALLOC_COUNT; - dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_callocN( - dynamic_vaos.count * sizeof(GPUShaderInterface *), "dyn vaos interfaces"); + dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_callocN( + dynamic_vaos.count * sizeof(GLShaderInterface *), "dyn vaos interfaces"); dynamic_vaos.vao_ids = (GLuint *)MEM_callocN(dynamic_vaos.count * sizeof(GLuint), "dyn vaos ids"); } @@ -118,8 +119,8 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) /* Not enough place, realloc the array. */ i = dynamic_vaos.count; dynamic_vaos.count += GPU_BATCH_VAO_DYN_ALLOC_COUNT; - dynamic_vaos.interfaces = (const GPUShaderInterface **)MEM_recallocN( - (void *)dynamic_vaos.interfaces, sizeof(GPUShaderInterface *) * dynamic_vaos.count); + dynamic_vaos.interfaces = (const GLShaderInterface **)MEM_recallocN( + (void *)dynamic_vaos.interfaces, sizeof(GLShaderInterface *) * dynamic_vaos.count); dynamic_vaos.vao_ids = (GLuint *)MEM_recallocN(dynamic_vaos.vao_ids, sizeof(GLuint) * dynamic_vaos.count); } @@ -127,15 +128,15 @@ void GLVaoCache::insert(const GPUShaderInterface *interface, GLuint vao) dynamic_vaos.vao_ids[i] = vao; } - GPU_shaderinterface_add_batch_ref(const_cast<GPUShaderInterface *>(interface), this); + const_cast<GLShaderInterface *>(interface)->ref_add(this); } -void GLVaoCache::remove(const GPUShaderInterface *interface) +void GLVaoCache::remove(const GLShaderInterface *interface) { const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids; - const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : - static_vaos.interfaces; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; for (int i = 0; i < count; i++) { if (interfaces[i] == interface) { context_->vao_free(vaos[i]); @@ -151,8 +152,8 @@ void GLVaoCache::clear(void) GLContext *ctx = static_cast<GLContext *>(GPU_context_active_get()); const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; GLuint *vaos = (is_dynamic_vao_count) ? dynamic_vaos.vao_ids : static_vaos.vao_ids; - const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : - static_vaos.interfaces; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; /* Early out, nothing to free. */ if (context_ == NULL) { return; @@ -171,10 +172,9 @@ void GLVaoCache::clear(void) } for (int i = 0; i < count; i++) { - if (interfaces[i] == NULL) { - continue; + if (interfaces[i] != NULL) { + const_cast<GLShaderInterface *>(interfaces[i])->ref_remove(this); } - GPU_shaderinterface_remove_batch_ref(const_cast<GPUShaderInterface *>(interfaces[i]), this); } if (is_dynamic_vao_count) { @@ -190,11 +190,11 @@ void GLVaoCache::clear(void) } /* Return 0 on cache miss (invalid VAO) */ -GLuint GLVaoCache::lookup(const GPUShaderInterface *interface) +GLuint GLVaoCache::lookup(const GLShaderInterface *interface) { const int count = (is_dynamic_vao_count) ? dynamic_vaos.count : GPU_VAO_STATIC_LEN; - const GPUShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : - static_vaos.interfaces; + const GLShaderInterface **interfaces = (is_dynamic_vao_count) ? dynamic_vaos.interfaces : + static_vaos.interfaces; for (int i = 0; i < count; i++) { if (interfaces[i] == interface) { return (is_dynamic_vao_count) ? dynamic_vaos.vao_ids[i] : static_vaos.vao_ids[i]; @@ -226,7 +226,9 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first) { this->context_check(); /* Make sure the interface is up to date. */ - if (interface_ != GPU_context_active_get()->shader->interface) { + Shader *shader = GPU_context_active_get()->shader; + GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface); + if (interface_ != interface) { vao_get(batch); /* Trigger update. */ base_instance_ = 0; @@ -238,6 +240,7 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first) #ifdef __APPLE__ glDeleteVertexArrays(1, &vao_base_instance_); vao_base_instance_ = 0; + base_instance_ = 0; #endif if (vao_base_instance_ == 0) { @@ -248,16 +251,17 @@ GLuint GLVaoCache::base_instance_vao_get(GPUBatch *batch, int i_first) base_instance_ = i_first; GLVertArray::update_bindings(vao_base_instance_, batch, interface_, i_first); } - return base_instance_; + return vao_base_instance_; } GLuint GLVaoCache::vao_get(GPUBatch *batch) { this->context_check(); - GPUContext *ctx = GPU_context_active_get(); - if (interface_ != ctx->shader->interface) { - interface_ = ctx->shader->interface; + Shader *shader = GPU_context_active_get()->shader; + GLShaderInterface *interface = static_cast<GLShaderInterface *>(shader->interface); + if (interface_ != interface) { + interface_ = interface; vao_id_ = this->lookup(interface_); if (vao_id_ == 0) { @@ -303,6 +307,7 @@ void GLBatch::bind(int i_first) GPU_context_active_get()->state_manager->apply_state(); if (flag & GPU_BATCH_DIRTY) { + flag &= ~GPU_BATCH_DIRTY; vao_cache_.clear(); } @@ -324,6 +329,8 @@ void GLBatch::bind(int i_first) void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) { + GL_CHECK_ERROR("Batch Pre drawing"); + this->bind(i_first); BLI_assert(v_count > 0 && i_count > 0); @@ -350,6 +357,7 @@ void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) glDrawElementsInstancedBaseVertex( gl_type, v_count, index_type, v_first_ofs, i_count, base_index); } + GL_CHECK_ERROR("Batch Post-drawing Indexed"); } else { #ifdef __APPLE__ @@ -364,6 +372,7 @@ void GLBatch::draw(int v_first, int v_count, int i_first, int i_count) #ifdef __APPLE__ glEnable(GL_PRIMITIVE_RESTART); #endif + GL_CHECK_ERROR("Batch Post-drawing Non-indexed"); } } diff --git a/source/blender/gpu/opengl/gl_batch.hh b/source/blender/gpu/opengl/gl_batch.hh index d70f43aed2a..9399148c68d 100644 --- a/source/blender/gpu/opengl/gl_batch.hh +++ b/source/blender/gpu/opengl/gl_batch.hh @@ -32,11 +32,12 @@ #include "glew-mx.h" -#include "GPU_shader_interface.h" - namespace blender { namespace gpu { +class GLContext; +class GLShaderInterface; + #define GPU_VAO_STATIC_LEN 3 /* Vao management: remembers all geometry state (vertex attribute bindings & element buffer) @@ -45,9 +46,9 @@ namespace gpu { class GLVaoCache { private: /** Context for which the vao_cache_ was generated. */ - struct GLContext *context_ = NULL; + GLContext *context_ = NULL; /** Last interface this batch was drawn with. */ - GPUShaderInterface *interface_ = NULL; + GLShaderInterface *interface_ = NULL; /** Cached vao for the last interface. */ GLuint vao_id_ = 0; /** Used whend arb_base_instance is not supported. */ @@ -58,13 +59,13 @@ class GLVaoCache { union { /** Static handle count */ struct { - const GPUShaderInterface *interfaces[GPU_VAO_STATIC_LEN]; + const GLShaderInterface *interfaces[GPU_VAO_STATIC_LEN]; GLuint vao_ids[GPU_VAO_STATIC_LEN]; } static_vaos; /** Dynamic handle count */ struct { uint count; - const GPUShaderInterface **interfaces; + const GLShaderInterface **interfaces; GLuint *vao_ids; } dynamic_vaos; }; @@ -76,9 +77,9 @@ class GLVaoCache { GLuint vao_get(GPUBatch *batch); GLuint base_instance_vao_get(GPUBatch *batch, int i_first); - GLuint lookup(const GPUShaderInterface *interface); - void insert(const GPUShaderInterface *interface, GLuint vao_id); - void remove(const GPUShaderInterface *interface); + GLuint lookup(const GLShaderInterface *interface); + void insert(const GLShaderInterface *interface, GLuint vao_id); + void remove(const GLShaderInterface *interface); void clear(void); private: diff --git a/source/blender/gpu/opengl/gl_context.cc b/source/blender/gpu/opengl/gl_context.cc index 11f313f639b..2ac361d28e1 100644 --- a/source/blender/gpu/opengl/gl_context.cc +++ b/source/blender/gpu/opengl/gl_context.cc @@ -22,6 +22,7 @@ */ #include "BLI_assert.h" +#include "BLI_system.h" #include "BLI_utildefines.h" #include "GPU_framebuffer.h" @@ -238,3 +239,37 @@ void GLContext::framebuffer_unregister(struct GPUFrameBuffer *fb) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Error Checking + * + * This is only useful for implementation that does not support the KHR_debug extension. + * \{ */ + +void GLContext::check_error(const char *info) +{ + GLenum error = glGetError(); + +#define ERROR_CASE(err) \ + case err: \ + fprintf(stderr, "GL error: %s : %s\n", #err, info); \ + BLI_system_backtrace(stderr); \ + break; + + switch (error) { + ERROR_CASE(GL_INVALID_ENUM) + ERROR_CASE(GL_INVALID_VALUE) + ERROR_CASE(GL_INVALID_OPERATION) + ERROR_CASE(GL_INVALID_FRAMEBUFFER_OPERATION) + ERROR_CASE(GL_OUT_OF_MEMORY) + ERROR_CASE(GL_STACK_UNDERFLOW) + ERROR_CASE(GL_STACK_OVERFLOW) + case GL_NO_ERROR: + break; + default: + fprintf(stderr, "Unknown GL error: %x : %s", error, info); + break; + } +} + +/** \} */ diff --git a/source/blender/gpu/opengl/gl_context.hh b/source/blender/gpu/opengl/gl_context.hh index 0b762c939f1..35121a960ba 100644 --- a/source/blender/gpu/opengl/gl_context.hh +++ b/source/blender/gpu/opengl/gl_context.hh @@ -32,13 +32,20 @@ #include "glew-mx.h" -#include "gl_batch.hh" - #include <mutex> +/* Enabled on MacOS by default since there is no support for debug callbacks. */ +#if defined(DEBUG) && defined(__APPLE__) +# define GL_CHECK_ERROR(info) GLContext::check_error(info) +#else +# define GL_CHECK_ERROR(info) +#endif + namespace blender { namespace gpu { +class GLVaoCache; + class GLSharedOrphanLists { public: /** Mutex for the bellow structures. */ @@ -51,7 +58,7 @@ class GLSharedOrphanLists { void orphans_clear(void); }; -struct GLContext : public GPUContext { +class GLContext : public GPUContext { /* TODO(fclem) these needs to become private. */ public: /** Default VAO for procedural draw calls. */ @@ -78,6 +85,8 @@ struct GLContext : public GPUContext { GLContext(void *ghost_window, GLSharedOrphanLists &shared_orphan_list); ~GLContext(); + static void check_error(const char *info); + void activate(void) override; void deactivate(void) override; diff --git a/source/blender/gpu/opengl/gl_drawlist.hh b/source/blender/gpu/opengl/gl_drawlist.hh index 4f085149388..b690b8f8a98 100644 --- a/source/blender/gpu/opengl/gl_drawlist.hh +++ b/source/blender/gpu/opengl/gl_drawlist.hh @@ -19,6 +19,9 @@ /** \file * \ingroup gpu + * + * Implementation of Multi Draw Indirect using OpenGL. + * Fallback if the needed extensions are not supported. */ #pragma once @@ -37,6 +40,9 @@ namespace blender { namespace gpu { +/** + * Implementation of Multi Draw Indirect using OpenGL. + **/ class GLDrawList : public DrawList { public: GLDrawList(int length); diff --git a/source/blender/gpu/opengl/gl_shader.cc b/source/blender/gpu/opengl/gl_shader.cc index ea33ff00d69..3ec818b53a6 100644 --- a/source/blender/gpu/opengl/gl_shader.cc +++ b/source/blender/gpu/opengl/gl_shader.cc @@ -29,6 +29,7 @@ #include "GPU_platform.h" #include "gl_shader.hh" +#include "gl_shader_interface.hh" using namespace blender; using namespace blender::gpu; @@ -48,7 +49,7 @@ GLShader::GLShader(const char *name) : Shader(name) #ifndef __APPLE__ if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { char sh_name[64]; - BLI_snprintf(sh_name, sizeof(sh_name), "ShaderProgram-%s", name); + SNPRINTF(sh_name, "ShaderProgram-%s", name); glObjectLabel(GL_PROGRAM, shader_program_, -1, sh_name); } #endif @@ -150,6 +151,7 @@ GLuint GLShader::create_shader_stage(GLenum gl_stage, MutableSpan<const char *> } if (!status) { glDeleteShader(shader); + compilation_failed_ = true; return 0; } @@ -192,6 +194,10 @@ void GLShader::fragment_shader_from_glsl(MutableSpan<const char *> sources) bool GLShader::finalize(void) { + if (compilation_failed_) { + return false; + } + glLinkProgram(shader_program_); GLint status; @@ -203,10 +209,7 @@ bool GLShader::finalize(void) return false; } - /* TODO(fclem) We need this to modify the image binding points using glUniform. - * This could be avoided using glProgramUniform in GL 4.1. */ - glUseProgram(shader_program_); - interface = GPU_shaderinterface_create(shader_program_); + interface = new GLShaderInterface(shader_program_); return true; } diff --git a/source/blender/gpu/opengl/gl_shader.hh b/source/blender/gpu/opengl/gl_shader.hh index b432a04abaa..a686014f4c5 100644 --- a/source/blender/gpu/opengl/gl_shader.hh +++ b/source/blender/gpu/opengl/gl_shader.hh @@ -19,9 +19,6 @@ /** \file * \ingroup gpu - * - * GPUDrawList is an API to do lots of similar draw-calls very fast using - * multi-draw-indirect. There is a fallback if the feature is not supported. */ #pragma once @@ -35,14 +32,19 @@ namespace blender { namespace gpu { +/** + * Implementation of shader compilation and uniforms handling using OpenGL. + **/ class GLShader : public Shader { private: /** Handle for full program (links shader stages below). */ GLuint shader_program_ = 0; - + /** Individual shader stages. */ GLuint vert_shader_ = 0; GLuint geom_shader_ = 0; GLuint frag_shader_ = 0; + /** True if any shader failed to compile. */ + bool compilation_failed_ = false; eGPUShaderTFBType transform_feedback_type_ = GPU_SHADER_TFB_NONE; diff --git a/source/blender/gpu/opengl/gl_shader_interface.cc b/source/blender/gpu/opengl/gl_shader_interface.cc new file mode 100644 index 00000000000..423db5c8c97 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_interface.cc @@ -0,0 +1,297 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 by Mike Erwin. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + */ + +#include "BLI_bitmap.h" + +#include "gl_batch.hh" + +#include "gl_shader_interface.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Binding assignment + * + * To mimic vulkan, we assign binding at shader creation to avoid shader recompilation. + * In the future, we should set it in the shader using layout(binding = i) and query its value. + * \{ */ + +static inline int block_binding(int32_t program, uint32_t block_index) +{ + /* For now just assign a consecutive index. In the future, we should set it in + * the shader using layout(binding = i) and query its value. */ + glUniformBlockBinding(program, block_index, block_index); + return block_index; +} + +static inline int sampler_binding(int32_t program, + uint32_t uniform_index, + int32_t uniform_location, + int *sampler_len) +{ + /* Identify sampler uniforms and asign sampler units to them. */ + GLint type; + glGetActiveUniformsiv(program, 1, &uniform_index, GL_UNIFORM_TYPE, &type); + + switch (type) { + case GL_SAMPLER_1D: + case GL_SAMPLER_2D: + case GL_SAMPLER_3D: + case GL_SAMPLER_CUBE: + case GL_SAMPLER_CUBE_MAP_ARRAY_ARB: /* OpenGL 4.0 */ + case GL_SAMPLER_1D_SHADOW: + case GL_SAMPLER_2D_SHADOW: + case GL_SAMPLER_1D_ARRAY: + case GL_SAMPLER_2D_ARRAY: + case GL_SAMPLER_1D_ARRAY_SHADOW: + case GL_SAMPLER_2D_ARRAY_SHADOW: + case GL_SAMPLER_2D_MULTISAMPLE: + case GL_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_SAMPLER_CUBE_SHADOW: + case GL_SAMPLER_BUFFER: + case GL_INT_SAMPLER_1D: + case GL_INT_SAMPLER_2D: + case GL_INT_SAMPLER_3D: + case GL_INT_SAMPLER_CUBE: + case GL_INT_SAMPLER_1D_ARRAY: + case GL_INT_SAMPLER_2D_ARRAY: + case GL_INT_SAMPLER_2D_MULTISAMPLE: + case GL_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_INT_SAMPLER_BUFFER: + case GL_UNSIGNED_INT_SAMPLER_1D: + case GL_UNSIGNED_INT_SAMPLER_2D: + case GL_UNSIGNED_INT_SAMPLER_3D: + case GL_UNSIGNED_INT_SAMPLER_CUBE: + case GL_UNSIGNED_INT_SAMPLER_1D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE: + case GL_UNSIGNED_INT_SAMPLER_2D_MULTISAMPLE_ARRAY: + case GL_UNSIGNED_INT_SAMPLER_BUFFER: { + /* For now just assign a consecutive index. In the future, we should set it in + * the shader using layout(binding = i) and query its value. */ + int binding = *sampler_len; + glUniform1i(uniform_location, binding); + (*sampler_len)++; + return binding; + } + default: + return -1; + } +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Creation / Destruction + * \{ */ + +GLShaderInterface::GLShaderInterface(GLuint program) +{ + /* Necessary to make glUniform works. */ + glUseProgram(program); + + GLint max_attr_name_len = 0, attr_len = 0; + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &max_attr_name_len); + glGetProgramiv(program, GL_ACTIVE_ATTRIBUTES, &attr_len); + + GLint max_ubo_name_len = 0, ubo_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH, &max_ubo_name_len); + glGetProgramiv(program, GL_ACTIVE_UNIFORM_BLOCKS, &ubo_len); + + GLint max_uniform_name_len = 0, active_uniform_len = 0, uniform_len = 0; + glGetProgramiv(program, GL_ACTIVE_UNIFORM_MAX_LENGTH, &max_uniform_name_len); + glGetProgramiv(program, GL_ACTIVE_UNIFORMS, &active_uniform_len); + uniform_len = active_uniform_len; + + /* Work around driver bug with Intel HD 4600 on Windows 7/8, where + * GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH does not work. */ + if (attr_len > 0 && max_attr_name_len == 0) { + max_attr_name_len = 256; + } + if (ubo_len > 0 && max_ubo_name_len == 0) { + max_ubo_name_len = 256; + } + if (uniform_len > 0 && max_uniform_name_len == 0) { + max_uniform_name_len = 256; + } + + /* GL_ACTIVE_UNIFORMS lied to us! Remove the UBO uniforms from the total before + * allocating the uniform array. */ + GLint max_ubo_uni_len = 0; + for (int i = 0; i < ubo_len; i++) { + GLint ubo_uni_len; + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); + max_ubo_uni_len = max_ii(max_ubo_uni_len, ubo_uni_len); + uniform_len -= ubo_uni_len; + } + /* Bit set to true if uniform comes from a uniform block. */ + BLI_bitmap *uniforms_from_blocks = BLI_BITMAP_NEW(active_uniform_len, __func__); + /* Set uniforms from block for exclusion. */ + GLint *ubo_uni_ids = (GLint *)MEM_mallocN(sizeof(GLint) * max_ubo_uni_len, __func__); + for (int i = 0; i < ubo_len; i++) { + GLint ubo_uni_len; + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS, &ubo_uni_len); + glGetActiveUniformBlockiv(program, i, GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES, ubo_uni_ids); + for (int u = 0; u < ubo_uni_len; u++) { + BLI_BITMAP_ENABLE(uniforms_from_blocks, ubo_uni_ids[u]); + } + } + MEM_freeN(ubo_uni_ids); + + int input_tot_len = attr_len + ubo_len + uniform_len; + inputs_ = (ShaderInput *)MEM_callocN(sizeof(ShaderInput) * input_tot_len, __func__); + + const uint32_t name_buffer_len = attr_len * max_attr_name_len + ubo_len * max_ubo_name_len + + uniform_len * max_uniform_name_len; + name_buffer_ = (char *)MEM_mallocN(name_buffer_len, "name_buffer"); + uint32_t name_buffer_offset = 0; + + /* Attributes */ + enabled_attr_mask_ = 0; + for (int i = 0; i < attr_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + GLenum type; + GLint size; + + glGetActiveAttrib(program, i, remaining_buffer, &name_len, &size, &type, name); + GLint location = glGetAttribLocation(program, name); + /* Ignore OpenGL names like `gl_BaseInstanceARB`, `gl_InstanceID` and `gl_VertexID`. */ + if (location == -1) { + continue; + } + + ShaderInput *input = &inputs_[attr_len_++]; + input->location = input->binding = location; + + name_buffer_offset += set_input_name(input, name, name_len); + enabled_attr_mask_ |= (1 << input->location); + } + + /* Uniform Blocks */ + for (int i = 0; i < ubo_len; i++) { + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + + glGetActiveUniformBlockName(program, i, remaining_buffer, &name_len, name); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_++]; + input->binding = input->location = block_binding(program, i); + + name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_ubo_mask_ |= (1 << input->binding); + } + + /* Uniforms */ + for (int i = 0, sampler = 0; i < active_uniform_len; i++) { + if (BLI_BITMAP_TEST(uniforms_from_blocks, i)) { + continue; + } + char *name = name_buffer_ + name_buffer_offset; + GLsizei remaining_buffer = name_buffer_len - name_buffer_offset; + GLsizei name_len = 0; + + glGetActiveUniformName(program, i, remaining_buffer, &name_len, name); + + ShaderInput *input = &inputs_[attr_len_ + ubo_len_ + uniform_len_++]; + input->location = glGetUniformLocation(program, name); + input->binding = sampler_binding(program, i, input->location, &sampler); + + name_buffer_offset += this->set_input_name(input, name, name_len); + enabled_tex_mask_ |= (input->binding != -1) ? (1lu << input->binding) : 0lu; + } + + /* Builtin Uniforms */ + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORMS; u_int++) { + GPUUniformBuiltin u = static_cast<GPUUniformBuiltin>(u_int); + builtins_[u] = glGetUniformLocation(program, builtin_uniform_name(u)); + } + + /* Builtin Uniforms Blocks */ + for (int32_t u_int = 0; u_int < GPU_NUM_UNIFORM_BLOCKS; u_int++) { + GPUUniformBlockBuiltin u = static_cast<GPUUniformBlockBuiltin>(u_int); + const ShaderInput *block = this->ubo_get(builtin_uniform_block_name(u)); + builtin_blocks_[u] = (block != NULL) ? block->binding : -1; + } + + MEM_freeN(uniforms_from_blocks); + + /* Resize name buffer to save some memory. */ + if (name_buffer_offset < name_buffer_len) { + name_buffer_ = (char *)MEM_reallocN(name_buffer_, name_buffer_offset); + } + + // this->debug_print(); + + this->sort_inputs(); +} + +GLShaderInterface::~GLShaderInterface() +{ + for (auto *ref : refs_) { + if (ref != NULL) { + ref->remove(this); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Batch Reference + * \{ */ + +void GLShaderInterface::ref_add(GLVaoCache *ref) +{ + for (int i = 0; i < refs_.size(); i++) { + if (refs_[i] == NULL) { + refs_[i] = ref; + return; + } + } + refs_.append(ref); +} + +void GLShaderInterface::ref_remove(GLVaoCache *ref) +{ + for (int i = 0; i < refs_.size(); i++) { + if (refs_[i] == ref) { + refs_[i] = NULL; + break; /* cannot have duplicates */ + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Validation + * TODO + * \{ */ + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_shader_interface.hh b/source/blender/gpu/opengl/gl_shader_interface.hh new file mode 100644 index 00000000000..0b9585aa389 --- /dev/null +++ b/source/blender/gpu/opengl/gl_shader_interface.hh @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + * + * GPU shader interface (C --> GLSL) + * + * Structure detailing needed vertex inputs and resources for a specific shader. + * A shader interface can be shared between two similar shaders. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "BLI_vector.hh" + +#include "glew-mx.h" + +#include "gpu_shader_interface.hh" + +namespace blender::gpu { + +class GLVaoCache; + +/** + * Implementation of Shader interface using OpenGL. + **/ +class GLShaderInterface : public ShaderInterface { + private: + /** Reference to VaoCaches using this interface */ + Vector<GLVaoCache *> refs_; + + public: + GLShaderInterface(GLuint program); + ~GLShaderInterface(); + + void ref_add(GLVaoCache *ref); + void ref_remove(GLVaoCache *ref); + + // bool resource_binding_validate(); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLShaderInterface"); +}; + +} // namespace blender::gpu diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc index 3e3695e0b48..7dc3e44c516 100644 --- a/source/blender/gpu/opengl/gl_state.cc +++ b/source/blender/gpu/opengl/gl_state.cc @@ -26,6 +26,7 @@ #include "glew-mx.h" +#include "gl_context.hh" #include "gl_state.hh" using namespace blender::gpu; @@ -53,6 +54,9 @@ GLStateManager::GLStateManager(void) : GPUStateManager() glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX); } + /* Limits. */ + glGetFloatv(GL_ALIASED_LINE_WIDTH_RANGE, line_width_range_); + /* Force update using default state. */ current_ = ~state; current_mutable_ = ~mutable_state; @@ -65,23 +69,23 @@ void GLStateManager::set_state(const GPUState &state) GPUState changed = state ^ current_; if (changed.blend != 0) { - set_blend(state.blend); + set_blend((eGPUBlend)state.blend); } if (changed.write_mask != 0) { - set_write_mask(state.write_mask); + set_write_mask((eGPUWriteMask)state.write_mask); } if (changed.depth_test != 0) { - set_depth_test(state.depth_test); + set_depth_test((eGPUDepthTest)state.depth_test); } if (changed.stencil_test != 0 || changed.stencil_op != 0) { - set_stencil_test(state.stencil_test, state.stencil_op); - set_stencil_mask(state.stencil_test, mutable_state); + set_stencil_test((eGPUStencilTest)state.stencil_test, (eGPUStencilOp)state.stencil_op); + set_stencil_mask((eGPUStencilTest)state.stencil_test, mutable_state); } if (changed.clip_distances != 0) { set_clip_distances(state.clip_distances, current_.clip_distances); } if (changed.culling_test != 0) { - set_backface_culling(state.culling_test); + set_backface_culling((eGPUFaceCullTest)state.culling_test); } if (changed.logic_op_xor != 0) { set_logic_op(state.logic_op_xor); @@ -90,7 +94,7 @@ void GLStateManager::set_state(const GPUState &state) set_facing(state.invert_facing); } if (changed.provoking_vert != 0) { - set_provoking_vert(state.provoking_vert); + set_provoking_vert((eGPUProvokingVertex)state.provoking_vert); } if (changed.shadow_bias != 0) { set_shadow_bias(state.shadow_bias); @@ -150,7 +154,7 @@ void GLStateManager::set_mutable_state(const GPUStateMutable &state) if (changed.line_width != 0) { /* TODO remove, should use wide line shader. */ - glLineWidth(clamp_f(state.line_width, 1.0f, GPU_max_line_width())); + glLineWidth(clamp_f(state.line_width, line_width_range_[0], line_width_range_[1])); } if (changed.depth_range[0] != 0 || changed.depth_range[1] != 0) { @@ -160,7 +164,7 @@ void GLStateManager::set_mutable_state(const GPUStateMutable &state) if (changed.stencil_compare_mask != 0 || changed.stencil_reference != 0 || changed.stencil_write_mask != 0) { - set_stencil_mask(current_.stencil_test, state); + set_stencil_mask((eGPUStencilTest)current_.stencil_test, state); } current_mutable_ = state; diff --git a/source/blender/gpu/opengl/gl_state.hh b/source/blender/gpu/opengl/gl_state.hh index b05fcc5d044..8e806cb3e7a 100644 --- a/source/blender/gpu/opengl/gl_state.hh +++ b/source/blender/gpu/opengl/gl_state.hh @@ -31,11 +31,17 @@ namespace blender { namespace gpu { +/** + * State manager keeping track of the draw state and applying it before drawing. + * Opengl Implementation. + **/ class GLStateManager : public GPUStateManager { private: /** Current state of the GL implementation. Avoids resetting the whole state for every change. */ GPUState current_; GPUStateMutable current_mutable_; + /** Limits. */ + float line_width_range_[2]; public: GLStateManager(); diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.cc b/source/blender/gpu/opengl/gl_uniform_buffer.cc new file mode 100644 index 00000000000..5115034639c --- /dev/null +++ b/source/blender/gpu/opengl/gl_uniform_buffer.cc @@ -0,0 +1,126 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#include "BKE_global.h" + +#include "BLI_string.h" + +#include "GPU_extensions.h" + +#include "gpu_backend.hh" +#include "gpu_context_private.hh" + +#include "gl_backend.hh" +#include "gl_uniform_buffer.hh" + +namespace blender::gpu { + +/* -------------------------------------------------------------------- */ +/** \name Creation & Deletion + * \{ */ + +GLUniformBuf::GLUniformBuf(size_t size, const char *name) : UniformBuf(size, name) +{ + /* Do not create ubo GL buffer here to allow allocation from any thread. */ +} + +GLUniformBuf::~GLUniformBuf() +{ + GLBackend::get()->buf_free(ubo_id_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Data upload / update + * \{ */ + +void GLUniformBuf::init(void) +{ + BLI_assert(GPU_context_active_get()); + + glGenBuffers(1, &ubo_id_); + glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_); + glBufferData(GL_UNIFORM_BUFFER, size_in_bytes_, NULL, GL_DYNAMIC_DRAW); + +#ifndef __APPLE__ + if ((G.debug & G_DEBUG_GPU) && (GLEW_VERSION_4_3 || GLEW_KHR_debug)) { + char sh_name[64]; + SNPRINTF(sh_name, "UBO-%s", name_); + glObjectLabel(GL_BUFFER, ubo_id_, -1, sh_name); + } +#endif +} + +void GLUniformBuf::update(const void *data) +{ + if (ubo_id_ == 0) { + this->init(); + } + glBindBuffer(GL_UNIFORM_BUFFER, ubo_id_); + glBufferSubData(GL_UNIFORM_BUFFER, 0, size_in_bytes_, data); + glBindBuffer(GL_UNIFORM_BUFFER, 0); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Usage + * \{ */ + +void GLUniformBuf::bind(int slot) +{ + if (slot >= GPU_max_ubo_binds()) { + fprintf(stderr, + "Error: Trying to bind \"%s\" ubo to slot %d which is above the reported limit of %d.", + name_, + slot, + GPU_max_ubo_binds()); + return; + } + + if (ubo_id_ == 0) { + this->init(); + } + + if (data_ != NULL) { + this->update(data_); + MEM_SAFE_FREE(data_); + } + + slot_ = slot; + glBindBufferBase(GL_UNIFORM_BUFFER, slot_, ubo_id_); +} + +void GLUniformBuf::unbind(void) +{ +#ifdef DEBUG + /* NOTE: This only unbinds the last bound slot. */ + glBindBufferBase(GL_UNIFORM_BUFFER, slot_, 0); +#endif + slot_ = 0; +} + +/** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_uniform_buffer.hh b/source/blender/gpu/opengl/gl_uniform_buffer.hh new file mode 100644 index 00000000000..b71356c4121 --- /dev/null +++ b/source/blender/gpu/opengl/gl_uniform_buffer.hh @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup gpu + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "gpu_uniform_buffer_private.hh" + +#include "glew-mx.h" + +namespace blender { +namespace gpu { + +/** + * Implementation of Uniform Buffers using OpenGL. + **/ +class GLUniformBuf : public UniformBuf { + private: + int slot_ = -1; + GLuint ubo_id_ = 0; + + public: + GLUniformBuf(size_t size, const char *name); + ~GLUniformBuf(); + + void update(const void *data) override; + void bind(int slot) override; + void unbind(void) override; + + private: + void init(void); + + MEM_CXX_CLASS_ALLOC_FUNCS("GLUniformBuf"); +}; + +} // namespace gpu +} // namespace blender diff --git a/source/blender/gpu/opengl/gl_vertex_array.cc b/source/blender/gpu/opengl/gl_vertex_array.cc index 907dc37e46f..b2d2445f113 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.cc +++ b/source/blender/gpu/opengl/gl_vertex_array.cc @@ -23,9 +23,9 @@ #include "GPU_glew.h" -#include "GPU_shader_interface.h" #include "GPU_vertex_buffer.h" +#include "gpu_shader_interface.hh" #include "gpu_vertex_format_private.h" #include "gl_batch.hh" @@ -33,14 +33,14 @@ #include "gl_vertex_array.hh" -using namespace blender::gpu; +namespace blender::gpu { /* -------------------------------------------------------------------- */ /** \name Vertex Array Bindings * \{ */ /* Returns enabled vertex pointers as a bitflag (one bit per attrib). */ -static uint16_t vbo_bind(const GPUShaderInterface *interface, +static uint16_t vbo_bind(const ShaderInterface *interface, const GPUVertFormat *format, uint v_first, uint v_len, @@ -68,7 +68,7 @@ static uint16_t vbo_bind(const GPUShaderInterface *interface, for (uint n_idx = 0; n_idx < a->name_len; n_idx++) { const char *name = GPU_vertformat_attr_name_get(format, a, n_idx); - const GPUShaderInput *input = GPU_shaderinterface_attr(interface, name); + const ShaderInput *input = interface->attr_get(name); if (input == NULL) { continue; @@ -111,10 +111,10 @@ static uint16_t vbo_bind(const GPUShaderInterface *interface, /* Update the Attrib Binding of the currently bound VAO. */ void GLVertArray::update_bindings(const GLuint vao, const GPUBatch *batch, - const GPUShaderInterface *interface, + const ShaderInterface *interface, const int base_instance) { - uint16_t attr_mask = interface->enabled_attr_mask; + uint16_t attr_mask = interface->enabled_attr_mask_; glBindVertexArray(vao); @@ -156,3 +156,5 @@ void GLVertArray::update_bindings(const GLuint vao, } /** \} */ + +} // namespace blender::gpu
\ No newline at end of file diff --git a/source/blender/gpu/opengl/gl_vertex_array.hh b/source/blender/gpu/opengl/gl_vertex_array.hh index 6da414d7e62..59cd50ad7b8 100644 --- a/source/blender/gpu/opengl/gl_vertex_array.hh +++ b/source/blender/gpu/opengl/gl_vertex_array.hh @@ -26,7 +26,7 @@ #include "glew-mx.h" #include "GPU_batch.h" -#include "GPU_shader_interface.h" +#include "gl_shader_interface.hh" namespace blender { namespace gpu { @@ -35,7 +35,7 @@ namespace GLVertArray { void update_bindings(const GLuint vao, const GPUBatch *batch, - const GPUShaderInterface *interface, + const ShaderInterface *interface, const int base_instance); } // namespace GLVertArray diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 2c42d59a2d9..6dd4d14cbc7 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -1267,12 +1267,12 @@ void IMB_colormanagement_check_file_config(Main *bmain) /* check sequencer strip input color space settings */ Sequence *seq; - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->strip) { colormanage_check_colorspace_settings(&seq->strip->colorspace_settings, "sequencer strip"); } } - SEQ_END; + SEQ_ALL_END; } /* ** check input color space settings ** */ diff --git a/source/blender/io/alembic/tests/abc_export_test.cc b/source/blender/io/alembic/tests/abc_export_test.cc index 5c2b505958e..c602868b07e 100644 --- a/source/blender/io/alembic/tests/abc_export_test.cc +++ b/source/blender/io/alembic/tests/abc_export_test.cc @@ -36,6 +36,8 @@ class AlembicExportTest : public testing::Test { bmain = BKE_main_new(); + DEG_register_node_types(); + /* TODO(sergey): Pass scene layer somehow? */ ViewLayer *view_layer = (ViewLayer *)scene.view_layers.first; depsgraph = DEG_graph_new(bmain, &scene, view_layer, DAG_EVAL_RENDER); @@ -45,6 +47,7 @@ class AlembicExportTest : public testing::Test { { BKE_main_free(bmain); DEG_graph_free(depsgraph); + DEG_free_node_types(); deleteArchive(); } diff --git a/source/blender/io/collada/BlenderContext.cpp b/source/blender/io/collada/BlenderContext.cpp index a9783a9b9c4..1d3bffacb79 100644 --- a/source/blender/io/collada/BlenderContext.cpp +++ b/source/blender/io/collada/BlenderContext.cpp @@ -123,7 +123,7 @@ bContext *BlenderContext::get_context() Depsgraph *BlenderContext::get_depsgraph() { if (!depsgraph) { - depsgraph = BKE_scene_get_depsgraph(main, scene, view_layer, true); + depsgraph = BKE_scene_ensure_depsgraph(main, scene, view_layer); } return depsgraph; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index feda4ba43eb..e16a22f5459 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -686,10 +686,6 @@ typedef enum IDRecalcFlag { ID_RECALC_PARAMETERS = (1 << 21), - /* Makes it so everything what depends on time. - * Basically, the same what changing frame in a timeline will do. */ - ID_RECALC_TIME = (1 << 22), - /* Input has changed and datablock is to be reload from disk. * Applies to movie clips to inform that copy-on-written version is to be refreshed for the new * input file or for color space changes. */ diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index 3873763e3e0..033a69d230e 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -343,6 +343,7 @@ typedef enum eBrushClothDeformType { BRUSH_CLOTH_DEFORM_PINCH_PERPENDICULAR = 4, BRUSH_CLOTH_DEFORM_INFLATE = 5, BRUSH_CLOTH_DEFORM_EXPAND = 6, + BRUSH_CLOTH_DEFORM_SNAKE_HOOK = 7, } eBrushClothDeformType; typedef enum eBrushSmoothDeformType { diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 37cda5f0e12..ac9766bc6a5 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -310,6 +310,7 @@ enum { REMESH_VOXEL = 0, REMESH_QUAD = 1, REMESH_TET = 2, + REMESH_TETLATTICE = 3, }; /* Subsurf Type */ diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index b92c9f42a73..2839d826df9 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -862,7 +862,8 @@ typedef struct BooleanModifierData { struct Object *object; char operation; - char _pad[2]; + char solver; + char _pad[1]; char bm_flag; float double_threshold; } BooleanModifierData; @@ -873,7 +874,12 @@ typedef enum { eBooleanModifierOp_Difference = 2, } BooleanModifierOp; -/* bm_flag (only used when G_DEBUG) */ +typedef enum { + eBooleanModifierSolver_Fast = 0, + eBooleanModifierSolver_Exact = 1, +} BooleanModifierSolver; + +/* bm_flag only used when G_DEBUG. */ enum { eBooleanModifierBMeshFlag_BMesh_Separate = (1 << 0), eBooleanModifierBMeshFlag_BMesh_NoDissolve = (1 << 1), diff --git a/source/blender/makesdna/DNA_object_force_types.h b/source/blender/makesdna/DNA_object_force_types.h index 33b16350354..da9ea45d5d4 100644 --- a/source/blender/makesdna/DNA_object_force_types.h +++ b/source/blender/makesdna/DNA_object_force_types.h @@ -193,8 +193,6 @@ typedef struct SBVertex { float vec[4]; } SBVertex; -typedef struct ADMMPDInterfaceData ADMMPDInterfaceData; - /* Container for data that is shared among CoW copies. * * This is placed in a separate struct so that values can be changed @@ -202,6 +200,7 @@ typedef struct ADMMPDInterfaceData ADMMPDInterfaceData; typedef struct SoftBody_Shared { struct PointCache *pointcache; struct ListBase ptcaches; + struct ListBase *admmpd_list; } SoftBody_Shared; typedef struct SoftBody { @@ -212,31 +211,28 @@ typedef struct SoftBody { /** Not saved in file. */ struct BodySpring *bspring; - struct ADMMPDInterfaceData *admmpd; - /* ADMM-PD settings */ - int solver_mode; // 0=legacy, 1=admmpd - int admmpd_mesh_mode; // 0=embedded, 1=tetgen, 2=cloth - int admmpd_substeps; // break time step into smaller bits - int admmpd_max_admm_iters; // max solver iterations - int admmpd_self_collision; // 0 or 1 - int admmpd_material; // see enum - int admmpd_embed_res; // embedded resolution depth - float admmpd_converge_eps; // convergence epsilon - float admmpd_youngs_exp; // Youngs mod exponent - float admmpd_poisson; // Poisson ratio - float admmpd_density_kgm3; // unit-density of object - float admmpd_ck_exp; // collision stiffness exponent (10^n) - float admmpd_pk_exp; // goal stiffness exponent (10^n) - float admmpd_floor_z; // floor position - float admmpd_gravity; // in m/s^2 - float admmpd_strainlimit_min; // [0,1] - float admmpd_strainlimit_max; // [1,100] - int admmpd_maxthreads; // -1 = auto - int admmpd_loglevel; // 0=none, 1=low, 2=high - int admmpd_linsolver; // global step + int solver_mode; /* 0=legacy, 0=admmpd */ + int admmpd_mesh_mode; /* 0=embedded, 1=tetgen, 2=cloth */ + int admmpd_substeps; /* break time step into smaller bits */ + int admmpd_max_admm_iters; /* max solver iterations */ + int admmpd_self_collision; /* 0 or 1 */ + int admmpd_material; /* see enum */ + int admmpd_embed_res; /* embedded resolution depth */ + float admmpd_converge_eps; /* convergence epsilon */ + float admmpd_youngs_exp; /* Youngs mod exponent */ + float admmpd_poisson; /* Poisson ratio */ + float admmpd_density_kgm3; /* unit-density of object */ + float admmpd_ck_exp; /* collision stiffness exponent (10^n) */ + float admmpd_pk_exp; /* goal stiffness exponent (10^n) */ + float admmpd_floor_z; /* floor position */ + float admmpd_gravity; /* in m/s^2 */ + float admmpd_strainlimit_min; /* [0,1] */ + float admmpd_strainlimit_max; /* [1,100] */ + int admmpd_maxthreads; /* -1 = auto */ + int admmpd_loglevel; /* 0=none, 1=low, 2=high */ + int admmpd_linsolver; /* global step */ char admmpd_namedVG_selfcollision[64]; - // int admmpd_pad; // padding char _pad; char msg_lock; @@ -435,8 +431,8 @@ typedef struct SoftBody { #define SBC_MODE_AVGMINMAX 4 /* sb->solver_mode */ -#define SOLVER_MODE_ADMMPD 0 -#define SOLVER_MODE_LEGACY 1 +#define SOLVER_MODE_LEGACY 0 +#define SOLVER_MODE_ADMMPD 1 #ifdef __cplusplus } diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 62c072831b4..586c704e0f1 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -108,15 +108,6 @@ enum { BOUNDBOX_DIRTY = (1 << 1), }; -typedef struct LodLevel { - struct LodLevel *next, *prev; - struct Object *source; - int flags; - float distance; - char _pad0[4]; - int obhysteresis; -} LodLevel; - struct CustomData_MeshMasks; /* Not saved in file! */ @@ -393,10 +384,6 @@ typedef struct Object { char empty_image_flag; char _pad8[5]; - /** Contains data for levels of detail. */ - ListBase lodlevels; - LodLevel *currentlod; - struct PreviewImage *preview; /** Runtime evaluation data (keep last). */ diff --git a/source/blender/makesdna/DNA_outliner_types.h b/source/blender/makesdna/DNA_outliner_types.h index 46c8b1570e3..52d466fdbdd 100644 --- a/source/blender/makesdna/DNA_outliner_types.h +++ b/source/blender/makesdna/DNA_outliner_types.h @@ -109,6 +109,8 @@ enum { #define TSE_SCENE_COLLECTION_BASE 39 #define TSE_VIEW_COLLECTION_BASE 40 #define TSE_SCENE_OBJECTS_BASE 41 +#define TSE_GPENCIL_EFFECT_BASE 42 +#define TSE_GPENCIL_EFFECT 43 /* Check whether given TreeStoreElem should have a real ID in its ->id member. */ #define TSE_IS_REAL_ID(_tse) \ diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index e8c4d5cde20..8e4063b36eb 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -534,9 +534,8 @@ typedef enum eScreen_Redraws_Flag { /** #Panel.flag */ enum { PNL_SELECT = (1 << 0), - PNL_CLOSEDX = (1 << 1), - PNL_CLOSEDY = (1 << 2), - PNL_CLOSED = (PNL_CLOSEDX | PNL_CLOSEDY), + PNL_UNUSED_1 = (1 << 1), /* Cleared */ + PNL_CLOSED = (1 << 2), /* PNL_TABBED = (1 << 3), */ /*UNUSED*/ /* PNL_OVERLAP = (1 << 4), */ /*UNUSED*/ PNL_PIN = (1 << 5), diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 136fe3744ef..ec46d2680ca 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -327,7 +327,7 @@ typedef struct ThemeSpace { unsigned char syntaxd[4], syntaxr[4]; // in nodespace used for distort unsigned char line_numbers[4]; - char _pad6[7]; + char _pad6[3]; unsigned char nodeclass_output[4], nodeclass_filter[4]; unsigned char nodeclass_vector[4], nodeclass_texture[4]; @@ -372,8 +372,6 @@ typedef struct ThemeSpace { /** Two uses, for uvs with modifier applied on mesh and uvs during painting. */ unsigned char uv_shadow[4]; - /** Uvs of other objects. */ - unsigned char uv_others[4]; /** Outliner - filter match. */ unsigned char match[4]; diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 7aaedbff1ce..29e29961028 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -1373,6 +1373,7 @@ static int make_structDNA(const char *base_directory, /* write a simple enum with all structs offsets, * should only be accessed via SDNA_TYPE_FROM_STRUCT macro */ { + fprintf(file_offsets, "#pragma once\n"); fprintf(file_offsets, "#define SDNA_TYPE_FROM_STRUCT(id) _SDNA_TYPE_##id\n"); fprintf(file_offsets, "enum {\n"); for (i = 0; i < structs_len; i++) { diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index 1896813bdb3..303005a0f9e 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -352,6 +352,10 @@ if(WITH_XR_OPENXR) add_definitions(-DWITH_XR_OPENXR) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + # Build makesrna executable blender_include_dirs( . diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index f48a7e6715d..0b923eb5635 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -2016,6 +2016,7 @@ static void rna_def_brush(BlenderRNA *brna) {BRUSH_CLOTH_DEFORM_INFLATE, "INFLATE", 0, "Inflate", ""}, {BRUSH_CLOTH_DEFORM_GRAB, "GRAB", 0, "Grab", ""}, {BRUSH_CLOTH_DEFORM_EXPAND, "EXPAND", 0, "Expand", ""}, + {BRUSH_CLOTH_DEFORM_SNAKE_HOOK, "SNAKE_HOOK", 0, "Snake Hook", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index cfc20db12f4..ab4d936ae34 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -623,13 +623,13 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, bool seq_found = false; if (&scene->sequencer_colorspace_settings != colorspace_settings) { - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { if (seq->strip && &seq->strip->colorspace_settings == colorspace_settings) { seq_found = true; break; } } - SEQ_END; + SEQ_ALL_END; } if (seq_found) { @@ -643,10 +643,10 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *bmain, BKE_sequence_invalidate_cache_preprocessed(scene, seq); } else { - SEQ_BEGIN (scene->ed, seq) { + SEQ_ALL_BEGIN (scene->ed, seq) { BKE_sequence_free_anim(seq); } - SEQ_END; + SEQ_ALL_END; } WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, NULL); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 6254e40a410..ad43202d850 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -455,10 +455,10 @@ void RNA_def_main_cachefiles(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_paintcurves(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_workspaces(BlenderRNA *brna, PropertyRNA *cprop); void RNA_def_main_lightprobes(BlenderRNA *brna, PropertyRNA *cprop); -#ifdef WITH_PARTICLE_NODES +#ifdef WITH_HAIR_NODES void RNA_def_main_hairs(BlenderRNA *brna, PropertyRNA *cprop); #endif -#ifdef WITH_HAIR_NODES +#ifdef WITH_PARTICLE_NODES void RNA_def_main_pointclouds(BlenderRNA *brna, PropertyRNA *cprop); #endif void RNA_def_main_volumes(BlenderRNA *brna, PropertyRNA *cprop); diff --git a/source/blender/makesrna/intern/rna_layer.c b/source/blender/makesrna/intern/rna_layer.c index e7a898b97ae..fc5e957bba6 100644 --- a/source/blender/makesrna/intern/rna_layer.c +++ b/source/blender/makesrna/intern/rna_layer.c @@ -184,10 +184,7 @@ static PointerRNA rna_ViewLayer_depsgraph_get(PointerRNA *ptr) if (GS(id->name) == ID_SCE) { Scene *scene = (Scene *)id; ViewLayer *view_layer = (ViewLayer *)ptr->data; - // NOTE: We don't allocate new depsgraph here, so the bmain is ignored. So it's easier to pass - // NULL. - // Still weak though. - Depsgraph *depsgraph = BKE_scene_get_depsgraph(NULL, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); return rna_pointer_inherit_refine(ptr, &RNA_Depsgraph, depsgraph); } return PointerRNA_NULL; @@ -206,7 +203,7 @@ static void rna_ViewLayer_update_tagged(ID *id_ptr, ReportList *reports) { Scene *scene = (Scene *)id_ptr; - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); if (DEG_is_evaluating(depsgraph)) { BKE_report(reports, RPT_ERROR, "Dependency graph update requested during evaluation"); diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 53de811eaa8..c5cceecb859 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -58,7 +58,8 @@ const EnumPropertyItem rna_enum_mesh_delimit_mode_items[] = { static const EnumPropertyItem rna_enum_mesh_remesh_mode_items[] = { {REMESH_VOXEL, "VOXEL", 0, "Voxel", "Use the voxel remesher"}, {REMESH_QUAD, "QUAD", 0, "Quad", "Use the quad remesher"}, - {REMESH_TET, "TET", 0, "Tet", "Use the tetrahedralize remesher"}, + {REMESH_TET, "TET", 0, "TetGen", "Use the tetrahedralize remesher"}, + {REMESH_TETLATTICE, "TETLATTICE", 0, "Tet lattice", "Use the tet-lattice remesher"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 0338a094d14..d9e151e5f73 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -2819,6 +2819,16 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem prop_solver_items[] = { + {eBooleanModifierSolver_Fast, + "FAST", + 0, + "Fast", + "Simple solver for the best performance, without support for overlapping geometry"}, + {eBooleanModifierSolver_Exact, "EXACT", 0, "Exact", "Advanced solver for the best result"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "BooleanModifier", "Modifier"); RNA_def_struct_ui_text(srna, "Boolean Modifier", "Boolean operations modifier"); RNA_def_struct_sdna(srna, "BooleanModifierData"); @@ -2847,6 +2857,12 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) prop, "Overlap Threshold", "Threshold for checking overlapping geometry"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "solver", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_solver_items); + RNA_def_property_enum_default(prop, eBooleanModifierSolver_Exact); + RNA_def_property_ui_text(prop, "Solver", "Method for calculating booleans"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + /* BMesh debugging options, only used when G_DEBUG is set */ /* BMesh intersection options */ diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index af07185ab4a..3237f33835e 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -5434,7 +5434,8 @@ static void def_sh_bevel(StructRNA *srna) prop = RNA_def_property(srna, "samples", PROP_INT, PROP_UNSIGNED); RNA_def_property_int_sdna(prop, NULL, "custom1"); - RNA_def_property_range(prop, 2, 16); + RNA_def_property_range(prop, 2, 128); + RNA_def_property_ui_range(prop, 2, 16, 1, 1); RNA_def_property_ui_text(prop, "Samples", "Number of rays to trace per shader evaluation"); RNA_def_property_update(prop, 0, "rna_Node_update"); } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 3ec7963a81e..e470a8ddb85 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -929,7 +929,7 @@ static const char *rna_Scene_statistics_string_get(Scene *scene, ReportList *reports, ViewLayer *view_layer) { - if (BKE_scene_find_from_view_layer(bmain, view_layer) != scene) { + if (!BKE_scene_has_view_layer(scene, view_layer)) { BKE_reportf(reports, RPT_ERROR, "View Layer '%s' not found in scene '%s'", @@ -1883,11 +1883,10 @@ static void object_simplify_update(Object *ob) } if (ob->instance_collection) { - CollectionObject *cob; - - for (cob = ob->instance_collection->gobject.first; cob; cob = cob->next) { - object_simplify_update(cob->ob); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (ob->instance_collection, ob_collection) { + object_simplify_update(ob_collection); } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } } @@ -5936,7 +5935,7 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_int_funcs(prop, "rna_RenderSettings_threads_get", NULL, NULL); RNA_def_property_ui_text(prop, "Threads", - "Number of CPU threads to use simultaneously while rendering " + "Maximum number of CPU cores to use simultaneously while rendering " "(for multi-core/CPU systems)"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index d258677c606..ff887e53965 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -72,7 +72,7 @@ static void rna_Scene_frame_set(Scene *scene, Main *bmain, int frame, float subf for (ViewLayer *view_layer = scene->view_layers.first; view_layer != NULL; view_layer = view_layer->next) { - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); BKE_scene_graph_update_for_newframe(depsgraph); } diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c index 28fdc5fb60f..e4c0ade1533 100644 --- a/source/blender/makesrna/intern/rna_space_api.c +++ b/source/blender/makesrna/intern/rna_space_api.c @@ -49,7 +49,7 @@ static void rna_RegionView3D_update(ID *id, RegionView3D *rv3d, bContext *C) if (WM_window_get_active_screen(win) == screen) { Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); ED_view3d_update_viewmat(depsgraph, scene, v3d, region, NULL, NULL, NULL, false); break; diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 90873fa73f4..699e08302e7 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -2478,6 +2478,11 @@ static void rna_def_userdef_theme_space_file(BlenderRNA *brna) RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Selected File", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "row_alternate", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_array(prop, 4); + RNA_def_property_ui_text(prop, "Alternate Rows", "Overlay color on every other row"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); } static void rna_def_userdef_theme_space_outliner(BlenderRNA *brna) @@ -3039,12 +3044,6 @@ static void rna_def_userdef_theme_space_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Texture paint/Modifier UVs", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); - prop = RNA_def_property(srna, "uv_others", PROP_FLOAT, PROP_COLOR_GAMMA); - RNA_def_property_float_sdna(prop, NULL, "uv_others"); - RNA_def_property_array(prop, 4); - RNA_def_property_ui_text(prop, "Other Object UVs", ""); - RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); - prop = RNA_def_property(srna, "frame_current", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "cframe"); RNA_def_property_array(prop, 3); diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index cf87e3598b6..d2ac9dc85de 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -171,6 +171,10 @@ if(WITH_CYCLES) add_definitions(-DWITH_CYCLES) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + # So we can have special tricks in modifier system. add_definitions(${GL_DEFINITIONS}) diff --git a/source/blender/modifiers/intern/MOD_boolean.c b/source/blender/modifiers/intern/MOD_boolean.c index 08fd7fb229d..0c8b8f6af3e 100644 --- a/source/blender/modifiers/intern/MOD_boolean.c +++ b/source/blender/modifiers/intern/MOD_boolean.c @@ -62,6 +62,7 @@ #include "bmesh.h" #include "bmesh_tools.h" +#include "tools/bmesh_boolean.h" #include "tools/bmesh_intersect.h" #ifdef DEBUG_TIME @@ -75,6 +76,11 @@ static void initData(ModifierData *md) bmd->double_threshold = 1e-6f; bmd->operation = eBooleanModifierOp_Difference; +#ifdef WITH_GMP + bmd->solver = eBooleanModifierSolver_Exact; +#else + bmd->solver = eBooleanModifierSolver_Fast; +#endif } static bool isDisabled(const struct Scene *UNUSED(scene), @@ -315,19 +321,30 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * 0; } - BM_mesh_intersect(bm, - looptris, - tottri, - bm_face_isect_pair, - NULL, - false, - use_separate, - use_dissolve, - use_island_connect, - false, - false, - bmd->operation, - bmd->double_threshold); +#ifdef WITH_GMP + bool use_exact = bmd->solver == eBooleanModifierSolver_Exact; +#else + bool use_exact = false; +#endif + + if (use_exact) { + BM_mesh_boolean(bm, looptris, tottri, bm_face_isect_pair, NULL, false, bmd->operation); + } + else { + BM_mesh_intersect(bm, + looptris, + tottri, + bm_face_isect_pair, + NULL, + false, + use_separate, + use_dissolve, + use_island_connect, + false, + false, + bmd->operation, + bmd->double_threshold); + } MEM_freeN(looptris); } @@ -374,7 +391,18 @@ static void panel_draw(const bContext *C, Panel *panel) uiLayoutSetPropSep(layout, true); uiItemR(layout, &ptr, "object", 0, NULL, ICON_NONE); - uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE); + +#ifdef WITH_GMP + bool use_exact = RNA_enum_get(&ptr, "solver") == eBooleanModifierSolver_Exact; +#else + bool use_exact = false; +#endif + if (!use_exact) { + uiItemR(layout, &ptr, "double_threshold", 0, NULL, ICON_NONE); + } +#ifdef WITH_GMP + uiItemR(layout, &ptr, "solver", UI_ITEM_R_EXPAND, NULL, ICON_NONE); +#endif if (G.debug) { uiLayout *col = uiLayoutColumn(layout, true); @@ -394,7 +422,7 @@ ModifierTypeInfo modifierType_Boolean = { /* structName */ "BooleanModifierData", /* structSize */ sizeof(BooleanModifierData), /* type */ eModifierTypeType_Nonconstructive, - /* flags */ eModifierTypeFlag_AcceptsMesh, + /* flags */ eModifierTypeFlag_AcceptsMesh | eModifierTypeFlag_SupportsEditmode, /* copyData */ BKE_modifier_copydata_generic, diff --git a/source/blender/modifiers/intern/MOD_meshdeform.c b/source/blender/modifiers/intern/MOD_meshdeform.c index ae031bffb04..4dee70608f8 100644 --- a/source/blender/modifiers/intern/MOD_meshdeform.c +++ b/source/blender/modifiers/intern/MOD_meshdeform.c @@ -37,7 +37,6 @@ #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_editmesh.h" -#include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_mesh.h" @@ -332,12 +331,7 @@ static void meshdeform_vert_task(void *__restrict userdata, if (totweight > 0.0f) { mul_v3_fl(co, fac / totweight); mul_m3_v3(data->icagemat, co); - if (G.debug_value != 527) { - add_v3_v3(vertexCos[iter], co); - } - else { - copy_v3_v3(vertexCos[iter], co); - } + add_v3_v3(vertexCos[iter], co); } } @@ -353,9 +347,8 @@ static void meshdeformModifier_do(ModifierData *md, Mesh *cagemesh; MDeformVert *dvert = NULL; float imat[4][4], cagemat[4][4], iobmat[4][4], icagemat[3][3], cmat[4][4]; - float co[3], (*dco)[3] = NULL, (*bindcagecos)[3]; + float(*dco)[3] = NULL, (*bindcagecos)[3]; int a, totvert, totcagevert, defgrp_index; - float(*cagecos)[3] = NULL; MeshdeformUserdata data; static int recursive_bind_sentinel = 0; @@ -406,7 +399,7 @@ static void meshdeformModifier_do(ModifierData *md, /* verify we have compatible weights */ totvert = numVerts; - totcagevert = cagemesh->totvert; + totcagevert = BKE_mesh_wrapper_vert_len(cagemesh); if (mmd->totvert != totvert) { BKE_modifier_set_error(md, "Vertices changed from %d to %d", mmd->totvert, totvert); @@ -422,27 +415,22 @@ static void meshdeformModifier_do(ModifierData *md, goto finally; } - /* setup deformation data */ - cagecos = BKE_mesh_vert_coords_alloc(cagemesh, NULL); - bindcagecos = (float(*)[3])mmd->bindcagecos; - /* We allocate 1 element extra to make it possible to * load the values to SSE registers, which are float4. */ dco = MEM_calloc_arrayN((totcagevert + 1), sizeof(*dco), "MDefDco"); zero_v3(dco[totcagevert]); + + /* setup deformation data */ + BKE_mesh_wrapper_vert_coords_copy(cagemesh, dco, totcagevert); + bindcagecos = (float(*)[3])mmd->bindcagecos; + for (a = 0; a < totcagevert; a++) { /* get cage vertex in world space with binding transform */ - copy_v3_v3(co, cagecos[a]); - - if (G.debug_value != 527) { - mul_m4_v3(mmd->bindmat, co); - /* compute difference with world space bind coord */ - sub_v3_v3v3(dco[a], co, bindcagecos[a]); - } - else { - copy_v3_v3(dco[a], co); - } + float co[3]; + mul_v3_m4v3(co, mmd->bindmat, dco[a]); + /* compute difference with world space bind coord */ + sub_v3_v3v3(dco[a], co, bindcagecos[a]); } MOD_get_vgroup(ob, mesh, mmd->defgrp_name, &dvert, &defgrp_index); @@ -464,7 +452,6 @@ static void meshdeformModifier_do(ModifierData *md, finally: MEM_SAFE_FREE(dco); - MEM_SAFE_FREE(cagecos); } static void deformVerts(ModifierData *md, diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index de192f51a5f..c1687e1a349 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -67,7 +67,7 @@ #include "GPU_material.h" #include "GPU_texture.h" -#include "GPU_uniformbuffer.h" +#include "GPU_uniform_buffer.h" #ifdef __cplusplus # include "FN_multi_function_builder.hh" diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.c b/source/blender/nodes/shader/nodes/node_shader_rgb.c index 47d9496c889..0bdef9a2a17 100644 --- a/source/blender/nodes/shader/nodes/node_shader_rgb.c +++ b/source/blender/nodes/shader/nodes/node_shader_rgb.c @@ -35,7 +35,7 @@ static int gpu_shader_rgb(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *link = GPU_uniformbuffer_link_out(mat, node, out, 0); + GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0); return GPU_stack_link(mat, node, "set_rgba", in, out, link); } diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 1d7c3f47233..1a1382fa8af 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -35,7 +35,7 @@ static int gpu_shader_value(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - GPUNodeLink *link = GPU_uniformbuffer_link_out(mat, node, out, 0); + GPUNodeLink *link = GPU_uniformbuf_link_out(mat, node, out, 0); return GPU_stack_link(mat, node, "set_value", in, out, link); } diff --git a/source/blender/python/bmesh/CMakeLists.txt b/source/blender/python/bmesh/CMakeLists.txt index 818498fe7db..89df127075d 100644 --- a/source/blender/python/bmesh/CMakeLists.txt +++ b/source/blender/python/bmesh/CMakeLists.txt @@ -64,4 +64,8 @@ if(WITH_FREESTYLE) add_definitions(-DWITH_FREESTYLE) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + blender_add_lib(bf_python_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/python/bmesh/bmesh_py_ops_call.c b/source/blender/python/bmesh/bmesh_py_ops_call.c index a387ba31c84..d0676ec1947 100644 --- a/source/blender/python/bmesh/bmesh_py_ops_call.c +++ b/source/blender/python/bmesh/bmesh_py_ops_call.c @@ -228,7 +228,7 @@ static int bpy_slot_from_py(BMesh *bm, break; } case BMO_OP_SLOT_FLT: { - float param = PyFloat_AsDouble(value); + const float param = PyFloat_AsDouble(value); if (param == -1 && PyErr_Occurred()) { PyErr_Format(PyExc_TypeError, "%.200s: keyword \"%.200s\" expected a float, not %.200s", @@ -840,7 +840,7 @@ PyObject *BPy_BMO_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw) { char slot_name_strip[MAX_SLOTNAME]; const char *ch = strchr(slot->slot_name, '.'); /* can't fail! */ - int tot = ch - slot->slot_name; + const int tot = ch - slot->slot_name; BLI_assert(ch != NULL); memcpy(slot_name_strip, slot->slot_name, tot); slot_name_strip[tot] = '\0'; diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index 04bceb17c20..2b174de7136 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1093,7 +1093,7 @@ static PyObject *bpy_bmesh_from_object(BPy_BMesh *self, PyObject *args, PyObject bool use_deform = true; bool use_cage = false; bool use_fnorm = true; - CustomData_MeshMasks data_masks = CD_MASK_BMESH; + const CustomData_MeshMasks data_masks = CD_MASK_BMESH; BPY_BM_CHECK_OBJ(self); @@ -1346,7 +1346,7 @@ static PyObject *bpy_bmesh_transform(BPy_BMElem *self, PyObject *args, PyObject } } else { - char filter_flags_ch = (char)filter_flags; + const char filter_flags_ch = (char)filter_flags; BM_ITER_MESH (eve, &iter, self->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, filter_flags_ch)) { mul_m4_v3((float(*)[4])mat_ptr, eve->co); @@ -3222,7 +3222,7 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key) { /* don't need error check here */ if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -3255,7 +3255,7 @@ static PyObject *bpy_bmelemseq_subscript(BPy_BMElemSeq *self, PyObject *key) if (start < 0 || stop < 0) { /* only get the length for negative values */ - Py_ssize_t len = bpy_bmelemseq_length(self); + const Py_ssize_t len = bpy_bmelemseq_length(self); if (start < 0) { start += len; } diff --git a/source/blender/python/bmesh/bmesh_py_types_customdata.c b/source/blender/python/bmesh/bmesh_py_types_customdata.c index 51616030d30..a9a9a3ad5d9 100644 --- a/source/blender/python/bmesh/bmesh_py_types_customdata.c +++ b/source/blender/python/bmesh/bmesh_py_types_customdata.c @@ -714,7 +714,7 @@ static PyObject *bpy_bmlayercollection_subscript_slice(BPy_BMLayerCollection *se Py_ssize_t start, Py_ssize_t stop) { - Py_ssize_t len = bpy_bmlayercollection_length(self); + const Py_ssize_t len = bpy_bmlayercollection_length(self); int count = 0; PyObject *tuple; @@ -746,7 +746,7 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py return bpy_bmlayercollection_subscript_str(self, _PyUnicode_AsString(key)); } if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -779,7 +779,7 @@ static PyObject *bpy_bmlayercollection_subscript(BPy_BMLayerCollection *self, Py if (start < 0 || stop < 0) { /* only get the length for negative values */ - Py_ssize_t len = bpy_bmlayercollection_length(self); + const Py_ssize_t len = bpy_bmlayercollection_length(self); if (start < 0) { start += len; } @@ -1127,7 +1127,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj } case CD_PROP_FLOAT: case CD_PAINT_MASK: { - float tmp_val = PyFloat_AsDouble(py_value); + const float tmp_val = PyFloat_AsDouble(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { PyErr_Format( PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name); @@ -1140,7 +1140,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj } case CD_PROP_INT32: case CD_FACEMAP: { - int tmp_val = PyC_Long_AsI32(py_value); + const int tmp_val = PyC_Long_AsI32(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { /* error is set */ ret = -1; @@ -1187,7 +1187,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj break; } case CD_BWEIGHT: { - float tmp_val = PyFloat_AsDouble(py_value); + const float tmp_val = PyFloat_AsDouble(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { PyErr_Format( PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name); @@ -1199,7 +1199,7 @@ int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer, PyObj break; } case CD_CREASE: { - float tmp_val = PyFloat_AsDouble(py_value); + const float tmp_val = PyFloat_AsDouble(py_value); if (UNLIKELY(tmp_val == -1 && PyErr_Occurred())) { PyErr_Format( PyExc_TypeError, "expected a float, not a %.200s", Py_TYPE(py_value)->tp_name); diff --git a/source/blender/python/bmesh/bmesh_py_types_select.c b/source/blender/python/bmesh/bmesh_py_types_select.c index d69668341ff..9bb9815f731 100644 --- a/source/blender/python/bmesh/bmesh_py_types_select.c +++ b/source/blender/python/bmesh/bmesh_py_types_select.c @@ -246,7 +246,7 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke { /* don't need error check here */ if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -279,7 +279,7 @@ static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *ke if (start < 0 || stop < 0) { /* only get the length for negative values */ - Py_ssize_t len = bpy_bmeditselseq_length(self); + const Py_ssize_t len = bpy_bmeditselseq_length(self); if (start < 0) { start += len; } diff --git a/source/blender/python/generic/bgl.c b/source/blender/python/generic/bgl.c index 405541554c9..89fe9f8c6aa 100644 --- a/source/blender/python/generic/bgl.c +++ b/source/blender/python/generic/bgl.c @@ -461,7 +461,7 @@ int BGL_typeSize(int type) static int gl_buffer_type_from_py_buffer(Py_buffer *pybuffer) { const char format = PyC_StructFmt_type_from_str(pybuffer->format); - Py_ssize_t itemsize = pybuffer->itemsize; + const Py_ssize_t itemsize = pybuffer->itemsize; if (PyC_StructFmt_type_is_float_any(format)) { if (itemsize == 4) { @@ -705,7 +705,7 @@ static int BGL_BufferOrOffsetConverter(PyObject *object, BufferOrOffset *buffer) return 1; } if (PyNumber_Check(object)) { - Py_ssize_t offset = PyNumber_AsSsize_t(object, PyExc_IndexError); + const Py_ssize_t offset = PyNumber_AsSsize_t(object, PyExc_IndexError); if (offset == -1 && PyErr_Occurred()) { return 0; } @@ -907,7 +907,7 @@ static int Buffer_ass_item(Buffer *self, int i, PyObject *v) Buffer *row = (Buffer *)Buffer_item(self, i); if (row) { - int ret = Buffer_ass_slice(row, 0, self->dimensions[1], v); + const int ret = Buffer_ass_slice(row, 0, self->dimensions[1], v); Py_DECREF(row); return ret; } diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 615ce514a3e..314a34e3dec 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -435,7 +435,7 @@ static IDProperty *idp_from_PyBytes(const char *name, PyObject *ob) static int idp_array_type_from_formatstr_and_size(const char *typestr, Py_ssize_t itemsize) { - char format = PyC_StructFmt_type_from_str(typestr); + const char format = PyC_StructFmt_type_from_str(typestr); if (PyC_StructFmt_type_is_float_any(format)) { if (itemsize == 4) { @@ -473,7 +473,7 @@ static IDProperty *idp_from_PySequence_Buffer(const char *name, Py_buffer *buffe IDProperty *prop; IDPropertyTemplate val = {0}; - int id_type = idp_array_type_from_formatstr_and_size(buffer->format, buffer->itemsize); + const int id_type = idp_array_type_from_formatstr_and_size(buffer->format, buffer->itemsize); if (id_type == -1) { /* should never happen as the type has been checked before */ return NULL; @@ -560,7 +560,7 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob) if (PyObject_CheckBuffer(ob)) { PyObject_GetBuffer(ob, &buffer, PyBUF_SIMPLE | PyBUF_FORMAT); - char format = PyC_StructFmt_type_from_str(buffer.format); + const char format = PyC_StructFmt_type_from_str(buffer.format); if (PyC_StructFmt_type_is_float_any(format) || (PyC_StructFmt_type_is_int_any(format) && buffer.itemsize == 4)) { use_buffer = true; @@ -589,7 +589,7 @@ static IDProperty *idp_from_PySequence(const char *name, PyObject *ob) static IDProperty *idp_from_PyMapping(const char *name, PyObject *ob) { IDProperty *prop; - IDPropertyTemplate val = {0}; + const IDPropertyTemplate val = {0}; PyObject *keys, *vals, *key, *pval; int i, len; @@ -1559,8 +1559,8 @@ static int itemsize_by_idarray_type(int array_type) static int BPy_IDArray_getbuffer(BPy_IDArray *self, Py_buffer *view, int flags) { IDProperty *prop = self->prop; - int itemsize = itemsize_by_idarray_type(prop->subtype); - int length = itemsize * prop->len; + const int itemsize = itemsize_by_idarray_type(prop->subtype); + const int length = itemsize * prop->len; if (PyBuffer_FillInfo(view, (PyObject *)self, IDP_Array(prop), length, false, flags) == -1) { return -1; diff --git a/source/blender/python/generic/imbuf_py_api.c b/source/blender/python/generic/imbuf_py_api.c index 3536236754e..5dc4aa6ce7c 100644 --- a/source/blender/python/generic/imbuf_py_api.c +++ b/source/blender/python/generic/imbuf_py_api.c @@ -260,7 +260,7 @@ static int py_imbuf_filepath_set(Py_ImBuf *self, PyObject *value, void *UNUSED(c } ImBuf *ibuf = self->ibuf; - Py_ssize_t value_str_len_max = sizeof(ibuf->name); + const Py_ssize_t value_str_len_max = sizeof(ibuf->name); Py_ssize_t value_str_len; const char *value_str = _PyUnicode_AsStringAndSize(value, &value_str_len); if (value_str_len >= value_str_len_max) { @@ -425,8 +425,8 @@ static PyObject *M_imbuf_new(PyObject *UNUSED(self), PyObject *args, PyObject *k } /* TODO, make options */ - uchar planes = 4; - uint flags = IB_rect; + const uchar planes = 4; + const uint flags = IB_rect; ImBuf *ibuf = IMB_allocImBuf(UNPACK2(size), planes, flags); if (ibuf == NULL) { @@ -500,7 +500,7 @@ static PyObject *M_imbuf_write(PyObject *UNUSED(self), PyObject *args, PyObject filepath = py_imb->ibuf->name; } - bool ok = IMB_saveiff(py_imb->ibuf, filepath, IB_rect); + const bool ok = IMB_saveiff(py_imb->ibuf, filepath, IB_rect); if (ok == false) { PyErr_Format( PyExc_IOError, "write: Unable to write image file (%s) '%s'", strerror(errno), filepath); diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 838a1239210..195442d34f6 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -207,7 +207,7 @@ PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len) */ void PyC_Tuple_Fill(PyObject *tuple, PyObject *value) { - uint tot = PyTuple_GET_SIZE(tuple); + const uint tot = PyTuple_GET_SIZE(tuple); uint i; for (i = 0; i < tot; i++) { @@ -218,7 +218,7 @@ void PyC_Tuple_Fill(PyObject *tuple, PyObject *value) void PyC_List_Fill(PyObject *list, PyObject *value) { - uint tot = PyList_GET_SIZE(list); + const uint tot = PyList_GET_SIZE(list); uint i; for (i = 0; i < tot; i++) { @@ -377,7 +377,7 @@ void PyC_StackSpit(void) } /* lame but handy */ - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); PyRun_SimpleString("__import__('traceback').print_stack()"); PyGILState_Release(gilstate); } @@ -948,7 +948,7 @@ void PyC_RunQuicky(const char *filepath, int n, ...) FILE *fp = fopen(filepath, "r"); if (fp) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); va_list vargs; @@ -1423,7 +1423,7 @@ bool PyC_RunString_AsString(const char *imports[], */ int PyC_Long_AsBool(PyObject *value) { - int test = _PyLong_AsInt(value); + const int test = _PyLong_AsInt(value); if (UNLIKELY((uint)test > 1)) { PyErr_SetString(PyExc_TypeError, "Python number not a bool (0/1)"); return -1; @@ -1433,7 +1433,7 @@ int PyC_Long_AsBool(PyObject *value) int8_t PyC_Long_AsI8(PyObject *value) { - int test = _PyLong_AsInt(value); + const int test = _PyLong_AsInt(value); if (UNLIKELY(test < INT8_MIN || test > INT8_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C int8"); return -1; @@ -1443,7 +1443,7 @@ int8_t PyC_Long_AsI8(PyObject *value) int16_t PyC_Long_AsI16(PyObject *value) { - int test = _PyLong_AsInt(value); + const int test = _PyLong_AsInt(value); if (UNLIKELY(test < INT16_MIN || test > INT16_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C int16"); return -1; @@ -1458,7 +1458,7 @@ int16_t PyC_Long_AsI16(PyObject *value) uint8_t PyC_Long_AsU8(PyObject *value) { - ulong test = PyLong_AsUnsignedLong(value); + const ulong test = PyLong_AsUnsignedLong(value); if (UNLIKELY(test > UINT8_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint8"); return (uint8_t)-1; @@ -1468,7 +1468,7 @@ uint8_t PyC_Long_AsU8(PyObject *value) uint16_t PyC_Long_AsU16(PyObject *value) { - ulong test = PyLong_AsUnsignedLong(value); + const ulong test = PyLong_AsUnsignedLong(value); if (UNLIKELY(test > UINT16_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint16"); return (uint16_t)-1; @@ -1478,7 +1478,7 @@ uint16_t PyC_Long_AsU16(PyObject *value) uint32_t PyC_Long_AsU32(PyObject *value) { - ulong test = PyLong_AsUnsignedLong(value); + const ulong test = PyLong_AsUnsignedLong(value); if (UNLIKELY(test > UINT32_MAX)) { PyErr_SetString(PyExc_OverflowError, "Python int too large to convert to C uint32"); return (uint32_t)-1; diff --git a/source/blender/python/gpu/gpu_py_offscreen.c b/source/blender/python/gpu/gpu_py_offscreen.c index e56f87e6221..15c39de990b 100644 --- a/source/blender/python/gpu/gpu_py_offscreen.c +++ b/source/blender/python/gpu/gpu_py_offscreen.c @@ -246,7 +246,7 @@ static PyObject *bpygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, BLI_assert(BKE_id_is_in_global_main(&scene->id)); - depsgraph = BKE_scene_get_depsgraph(G_MAIN, scene, view_layer, true); + depsgraph = BKE_scene_ensure_depsgraph(G_MAIN, scene, view_layer); rv3d_mats = ED_view3d_mats_rv3d_backup(region->regiondata); diff --git a/source/blender/python/gpu/gpu_py_shader.c b/source/blender/python/gpu/gpu_py_shader.c index c1a6ce09d37..f9ff0558570 100644 --- a/source/blender/python/gpu/gpu_py_shader.c +++ b/source/blender/python/gpu/gpu_py_shader.c @@ -78,7 +78,7 @@ static int bpygpu_uniform_location_get(GPUShader *shader, const char *name, const char *error_prefix) { - int uniform = GPU_shader_get_uniform(shader, name); + const int uniform = GPU_shader_get_uniform(shader, name); if (uniform == -1) { PyErr_Format(PyExc_ValueError, "%s: uniform %.32s not found", error_prefix, name); @@ -158,7 +158,7 @@ static PyObject *bpygpu_shader_uniform_from_name(BPyGPUShader *self, PyObject *a return NULL; } - int uniform = bpygpu_uniform_location_get(self->shader, name, "GPUShader.get_uniform"); + const int uniform = bpygpu_uniform_location_get(self->shader, name, "GPUShader.get_uniform"); if (uniform == -1) { return NULL; @@ -184,7 +184,7 @@ static PyObject *bpygpu_shader_uniform_block_from_name(BPyGPUShader *self, PyObj return NULL; } - int uniform = GPU_shader_get_uniform_block(self->shader, name); + const int uniform = GPU_shader_get_uniform_block(self->shader, name); if (uniform == -1) { PyErr_Format(PyExc_ValueError, "GPUShader.get_uniform_block: uniform %.32s not found", name); @@ -504,7 +504,7 @@ static PyObject *bpygpu_shader_attr_from_name(BPyGPUShader *self, PyObject *arg) return NULL; } - int attr = GPU_shader_get_attribute(self->shader, name); + const int attr = GPU_shader_get_attribute(self->shader, name); if (attr == -1) { PyErr_Format(PyExc_ValueError, "GPUShader.attr_from_name: attribute %.32s not found", name); diff --git a/source/blender/python/gpu/gpu_py_vertex_buffer.c b/source/blender/python/gpu/gpu_py_vertex_buffer.c index 57290fdc3c4..9372770e45e 100644 --- a/source/blender/python/gpu/gpu_py_vertex_buffer.c +++ b/source/blender/python/gpu/gpu_py_vertex_buffer.c @@ -134,7 +134,7 @@ static bool bpygpu_vertbuf_fill_impl(GPUVertBuf *vbo, return false; } - uint comp_len = pybuffer.ndim == 1 ? 1 : (uint)pybuffer.shape[1]; + const uint comp_len = pybuffer.ndim == 1 ? 1 : (uint)pybuffer.shape[1]; if (pybuffer.shape[0] != vbo->vertex_len) { PyErr_Format( diff --git a/source/blender/python/gpu/gpu_py_vertex_format.c b/source/blender/python/gpu/gpu_py_vertex_format.c index d8266be7e2c..1cbcaba6bfb 100644 --- a/source/blender/python/gpu/gpu_py_vertex_format.c +++ b/source/blender/python/gpu/gpu_py_vertex_format.c @@ -112,7 +112,7 @@ static int bpygpu_ParseVertCompType(PyObject *o, void *p) return 0; } - int comp_type = bpygpu_parse_component_type(str, length); + const int comp_type = bpygpu_parse_component_type(str, length); if (comp_type == -1) { PyErr_Format(PyExc_ValueError, "unknown component type: '%s", str); return 0; @@ -132,7 +132,7 @@ static int bpygpu_ParseVertFetchMode(PyObject *o, void *p) return 0; } - int fetch_mode = bpygpu_parse_fetch_mode(str, length); + const int fetch_mode = bpygpu_parse_fetch_mode(str, length); if (fetch_mode == -1) { PyErr_Format(PyExc_ValueError, "unknown type literal: '%s'", str); return 0; diff --git a/source/blender/python/intern/bpy_app.c b/source/blender/python/intern/bpy_app.c index 4ee936aff91..f0de05f95b3 100644 --- a/source/blender/python/intern/bpy_app.c +++ b/source/blender/python/intern/bpy_app.c @@ -343,7 +343,7 @@ static PyObject *bpy_app_debug_value_get(PyObject *UNUSED(self), void *UNUSED(cl static int bpy_app_debug_value_set(PyObject *UNUSED(self), PyObject *value, void *UNUSED(closure)) { - short param = PyC_Long_AsI16(value); + const short param = PyC_Long_AsI16(value); if (param == -1 && PyErr_Occurred()) { PyC_Err_SetString_Prefix(PyExc_TypeError, diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index cdbd3bc0b9c..a874e23ff32 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -318,7 +318,7 @@ void bpy_app_generic_callback(struct Main *UNUSED(main), { PyObject *cb_list = py_cb_array[POINTER_AS_INT(arg)]; if (PyList_GET_SIZE(cb_list) > 0) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); const int num_arguments = 2; PyObject *args_all = PyTuple_New(num_arguments); /* save python creating each call */ diff --git a/source/blender/python/intern/bpy_app_icons.c b/source/blender/python/intern/bpy_app_icons.c index 2e688609961..7cca3ae4700 100644 --- a/source/blender/python/intern/bpy_app_icons.c +++ b/source/blender/python/intern/bpy_app_icons.c @@ -71,8 +71,8 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a return NULL; } - int coords_size = sizeof(uchar[2]) * tris_len * 3; - int colors_size = sizeof(uchar[4]) * tris_len * 3; + const int coords_size = sizeof(uchar[2]) * tris_len * 3; + const int colors_size = sizeof(uchar[4]) * tris_len * 3; uchar(*coords)[2] = MEM_mallocN(coords_size, __func__); uchar(*colors)[4] = MEM_mallocN(colors_size, __func__); @@ -86,7 +86,7 @@ static PyObject *bpy_app_icons_new_triangles(PyObject *UNUSED(self), PyObject *a geom->coords = coords; geom->colors = colors; geom->icon_id = 0; - int icon_id = BKE_icon_geom_ensure(geom); + const int icon_id = BKE_icon_geom_ensure(geom); return PyLong_FromLong(icon_id); } @@ -117,7 +117,7 @@ static PyObject *bpy_app_icons_new_triangles_from_file(PyObject *UNUSED(self), PyErr_SetString(PyExc_ValueError, "Unable to load from file"); return NULL; } - int icon_id = BKE_icon_geom_ensure(geom); + const int icon_id = BKE_icon_geom_ensure(geom); return PyLong_FromLong(icon_id); } diff --git a/source/blender/python/intern/bpy_app_opensubdiv.c b/source/blender/python/intern/bpy_app_opensubdiv.c index 3f14c4dca57..09cd6201831 100644 --- a/source/blender/python/intern/bpy_app_opensubdiv.c +++ b/source/blender/python/intern/bpy_app_opensubdiv.c @@ -63,7 +63,7 @@ static PyObject *make_opensubdiv_info(void) #define SetObjItem(obj) PyStructSequence_SET_ITEM(opensubdiv_info, pos++, obj) #ifdef WITH_OPENSUBDIV - int curversion = openSubdiv_getVersionHex(); + const int curversion = openSubdiv_getVersionHex(); SetObjItem(PyBool_FromLong(1)); SetObjItem(PyC_Tuple_Pack_I32(curversion / 10000, (curversion / 100) % 100, curversion % 100)); SetObjItem(PyUnicode_FromFormat( diff --git a/source/blender/python/intern/bpy_app_timers.c b/source/blender/python/intern/bpy_app_timers.c index f1dd8b9e803..af299952b72 100644 --- a/source/blender/python/intern/bpy_app_timers.c +++ b/source/blender/python/intern/bpy_app_timers.c @@ -65,7 +65,7 @@ static double py_timer_execute(uintptr_t UNUSED(uuid), void *user_data) gilstate = PyGILState_Ensure(); PyObject *py_ret = PyObject_CallObject(function, NULL); - double ret = handle_returned_value(function, py_ret); + const double ret = handle_returned_value(function, py_ret); PyGILState_Release(gilstate); @@ -151,7 +151,7 @@ PyDoc_STRVAR(bpy_app_timers_is_registered_doc, " :rtype: bool\n"); static PyObject *bpy_app_timers_is_registered(PyObject *UNUSED(self), PyObject *function) { - bool ret = BLI_timer_is_registered((intptr_t)function); + const bool ret = BLI_timer_is_registered((intptr_t)function); return PyBool_FromLong(ret); } diff --git a/source/blender/python/intern/bpy_app_translations.c b/source/blender/python/intern/bpy_app_translations.c index c152c920453..f95261df6b2 100644 --- a/source/blender/python/intern/bpy_app_translations.c +++ b/source/blender/python/intern/bpy_app_translations.c @@ -92,7 +92,7 @@ static GHashKey *_ghashutil_keyalloc(const void *msgctxt, const void *msgid) static uint _ghashutil_keyhash(const void *ptr) { const GHashKey *key = ptr; - uint hash = BLI_ghashutil_strhash(key->msgctxt); + const uint hash = BLI_ghashutil_strhash(key->msgctxt); return hash ^ BLI_ghashutil_strhash(key->msgid); } diff --git a/source/blender/python/intern/bpy_driver.c b/source/blender/python/intern/bpy_driver.c index 7fb4b0c469c..4ef685b7987 100644 --- a/source/blender/python/intern/bpy_driver.c +++ b/source/blender/python/intern/bpy_driver.c @@ -228,7 +228,7 @@ static void bpy_pydriver_namespace_clear_self(void) void BPY_driver_reset(void) { PyGILState_STATE gilstate; - bool use_gil = true; /* !PyC_IsInterpreterActive(); */ + const bool use_gil = true; /* !PyC_IsInterpreterActive(); */ if (use_gil) { gilstate = PyGILState_Ensure(); @@ -594,7 +594,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna, #endif { /* try to get variable value */ - float tval = driver_get_variable_value(driver, dvar); + const float tval = driver_get_variable_value(driver, dvar); driver_arg = PyFloat_FromDouble((double)tval); } diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index b0b36baa839..bc7318e1a15 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -169,7 +169,7 @@ void BPY_text_free_code(Text *text) { if (text->compiled) { PyGILState_STATE gilstate; - bool use_gil = !PyC_IsInterpreterActive(); + const bool use_gil = !PyC_IsInterpreterActive(); if (use_gil) { gilstate = PyGILState_Ensure(); @@ -446,14 +446,14 @@ void BPY_python_backtrace(FILE *fp) void BPY_DECREF(void *pyob_ptr) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); Py_DECREF((PyObject *)pyob_ptr); PyGILState_Release(gilstate); } void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); const int do_invalidate = (Py_REFCNT((PyObject *)pyob_ptr) > 1); Py_DECREF((PyObject *)pyob_ptr); if (do_invalidate) { @@ -509,7 +509,7 @@ void BPY_modules_load_user(bContext *C) int BPY_context_member_get(bContext *C, const char *member, bContextDataResult *result) { PyGILState_STATE gilstate; - bool use_gil = !PyC_IsInterpreterActive(); + const bool use_gil = !PyC_IsInterpreterActive(); PyObject *pyctx; PyObject *item; @@ -544,7 +544,7 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * PyErr_Clear(); } else { - int len = PySequence_Fast_GET_SIZE(seq_fast); + const int len = PySequence_Fast_GET_SIZE(seq_fast); PyObject **seq_fast_items = PySequence_Fast_ITEMS(seq_fast); int i; diff --git a/source/blender/python/intern/bpy_library_load.c b/source/blender/python/intern/bpy_library_load.c index bcf13b1d88f..bdad4d03ae7 100644 --- a/source/blender/python/intern/bpy_library_load.c +++ b/source/blender/python/intern/bpy_library_load.c @@ -321,7 +321,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) { Main *bmain = CTX_data_main(BPy_GetContext()); Main *mainl = NULL; - int err = 0; + const int err = 0; const bool do_append = ((self->flag & FILE_LINK) == 0); BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); @@ -338,7 +338,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) // printf("lib: %s\n", name_plural); if (ls && PyList_Check(ls)) { /* loop */ - Py_ssize_t size = PyList_GET_SIZE(ls); + const Py_ssize_t size = PyList_GET_SIZE(ls); Py_ssize_t i; for (i = 0; i < size; i++) { @@ -423,7 +423,7 @@ static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) const char *name_plural = BKE_idtype_idcode_to_name_plural(idcode); PyObject *ls = PyDict_GetItemString(self->dict, name_plural); if (ls && PyList_Check(ls)) { - Py_ssize_t size = PyList_GET_SIZE(ls); + const Py_ssize_t size = PyList_GET_SIZE(ls); Py_ssize_t i; PyObject *item; diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c index 45c5aba1e3e..3739f56dc79 100644 --- a/source/blender/python/intern/bpy_msgbus.c +++ b/source/blender/python/intern/bpy_msgbus.c @@ -192,7 +192,7 @@ static void bpy_msgbus_notify(bContext *C, static void bpy_msgbus_subscribe_value_free_data(struct wmMsgSubscribeKey *UNUSED(msg_key), struct wmMsgSubscribeValue *msg_val) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); Py_DECREF(msg_val->owner); Py_DECREF(msg_val->user_data); PyGILState_Release(gilstate); diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index 66c67ca061c..859f0027f14 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -351,7 +351,7 @@ static bool bpy_prop_boolean_get_cb(struct PointerRNA *ptr, struct PropertyRNA * value = false; } else { - int value_i = PyC_Long_AsBool(ret); + const int value_i = PyC_Long_AsBool(ret); if (value_i == -1 && PyErr_Occurred()) { PyC_Err_PrintWithFunc(py_func); @@ -443,7 +443,7 @@ static bool bpy_prop_poll_cb(struct PointerRNA *self, PyObject *ret; bool result; const int is_write_ok = pyrna_write_check(); - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); BLI_assert(self != NULL); @@ -560,7 +560,7 @@ static void bpy_prop_boolean_array_set_cb(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); - int len = RNA_property_array_length(ptr, prop); + const int len = RNA_property_array_length(ptr, prop); BLI_assert(py_data != NULL); @@ -804,7 +804,7 @@ static void bpy_prop_int_array_set_cb(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); - int len = RNA_property_array_length(ptr, prop); + const int len = RNA_property_array_length(ptr, prop); BLI_assert(py_data != NULL); @@ -1048,7 +1048,7 @@ static void bpy_prop_float_array_set_cb(struct PointerRNA *ptr, PyGILState_STATE gilstate; bool use_gil; const bool is_write_ok = pyrna_write_check(); - int len = RNA_property_array_length(ptr, prop); + const int len = RNA_property_array_length(ptr, prop); BLI_assert(py_data != NULL); diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 955a24bc880..a3ded8813ac 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -464,7 +464,7 @@ static int mathutils_rna_vector_set(BaseMathObject *bmo, int subtype) if (subtype == MATHUTILS_CB_SUBTYPE_EUL) { EulerObject *eul = (EulerObject *)bmo; PropertyRNA *prop_eul_order = NULL; - short order = pyrna_rotation_euler_order_get(&self->ptr, eul->order, &prop_eul_order); + const short order = pyrna_rotation_euler_order_get(&self->ptr, eul->order, &prop_eul_order); if (order != eul->order) { RNA_property_enum_set(&self->ptr, prop_eul_order, eul->order); if (RNA_property_update_check(prop_eul_order)) { @@ -599,7 +599,7 @@ static short pyrna_rotation_euler_order_get(PointerRNA *ptr, } if (*r_prop_eul_order) { - short order = RNA_property_enum_get(ptr, *r_prop_eul_order); + const short order = RNA_property_enum_get(ptr, *r_prop_eul_order); /* Could be quat or axisangle. */ if (order >= EULER_ORDER_XYZ && order <= EULER_ORDER_ZYX) { return order; @@ -714,7 +714,8 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) /* Attempt to get order, * only needed for thick types since wrapped with update via callbacks. */ PropertyRNA *prop_eul_order = NULL; - short order = pyrna_rotation_euler_order_get(ptr, EULER_ORDER_XYZ, &prop_eul_order); + const short order = pyrna_rotation_euler_order_get( + ptr, EULER_ORDER_XYZ, &prop_eul_order); ret = Euler_CreatePyObject(NULL, order, NULL); /* TODO, get order from RNA. */ RNA_property_float_get_array(ptr, prop, ((EulerObject *)ret)->eul); @@ -1725,7 +1726,7 @@ static int pyrna_py_to_prop( } case PROP_INT: { int overflow; - long param = PyLong_AsLongAndOverflow(value, &overflow); + const long param = PyLong_AsLongAndOverflow(value, &overflow); if (overflow || (param > INT_MAX) || (param < INT_MIN)) { PyErr_Format(PyExc_ValueError, "%.200s %.200s.%.200s value not in 'int' range " @@ -1757,7 +1758,7 @@ static int pyrna_py_to_prop( break; } case PROP_FLOAT: { - float param = PyFloat_AsDouble(value); + const float param = PyFloat_AsDouble(value); if (PyErr_Occurred()) { PyErr_Format(PyExc_TypeError, "%.200s %.200s.%.200s expected a float type, not %.200s", @@ -1935,8 +1936,8 @@ static int pyrna_py_to_prop( PyObject *value_new = NULL; StructRNA *ptr_type = RNA_property_pointer_type(ptr, prop); - int flag = RNA_property_flag(prop); - int flag_parameter = RNA_parameter_flag(prop); + const int flag = RNA_property_flag(prop); + const int flag_parameter = RNA_parameter_flag(prop); /* This is really nasty! Done so we can fake the operator having direct properties, eg: * layout.prop(self, "filepath") @@ -2075,7 +2076,7 @@ static int pyrna_py_to_prop( BKE_reports_init(&reports, RPT_STORE); RNA_property_pointer_set( ptr, prop, value == Py_None ? PointerRNA_NULL : param->ptr, &reports); - int err = (BPy_reports_to_error(&reports, PyExc_RuntimeError, true)); + const int err = (BPy_reports_to_error(&reports, PyExc_RuntimeError, true)); if (err == -1) { Py_XDECREF(value_new); return -1; @@ -2233,7 +2234,7 @@ static int pyrna_py_to_prop_array_index(BPy_PropertyArrayRNA *self, int index, P /* See if we can coerce into a Python type - 'PropertyType'. */ switch (RNA_property_type(prop)) { case PROP_BOOLEAN: { - int param = PyC_Long_AsBool(value); + const int param = PyC_Long_AsBool(value); if (param == -1) { /* Error is set. */ @@ -2698,7 +2699,7 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject return pyrna_prop_collection_subscript_str(self, _PyUnicode_AsString(key)); } if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -2732,7 +2733,7 @@ static PyObject *pyrna_prop_collection_subscript(BPy_PropertyRNA *self, PyObject if (start < 0 || stop < 0) { /* Only get the length for negative values. */ - Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop); + const Py_ssize_t len = (Py_ssize_t)RNA_property_collection_length(&self->ptr, self->prop); if (start < 0) { start += len; } @@ -2827,7 +2828,7 @@ static int pyrna_prop_collection_ass_subscript(BPy_PropertyRNA *self, else #endif if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return -1; } @@ -2899,7 +2900,7 @@ static PyObject *pyrna_prop_array_subscript(BPy_PropertyArrayRNA *self, PyObject else #endif if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { return NULL; } @@ -2919,11 +2920,11 @@ static PyObject *pyrna_prop_array_subscript(BPy_PropertyArrayRNA *self, PyObject if (key_slice->start == Py_None && key_slice->stop == Py_None) { /* Note: no significant advantage with optimizing [:] slice as with collections, * but include here for consistency with collection slice func */ - Py_ssize_t len = (Py_ssize_t)pyrna_prop_array_length(self); + const Py_ssize_t len = (Py_ssize_t)pyrna_prop_array_length(self); return pyrna_prop_array_subscript_slice(self, &self->ptr, self->prop, 0, len, len); } - int len = pyrna_prop_array_length(self); + const int len = pyrna_prop_array_length(self); Py_ssize_t start, stop, slicelength; if (PySlice_GetIndicesEx(key, len, &start, &stop, &step, &slicelength) < 0) { @@ -3055,7 +3056,7 @@ static int prop_subscript_ass_array_slice__bool_recursive(PyObject **value_items BLI_assert(totdim == 1); int i; for (i = 0; i != length; i++) { - int v = PyLong_AsLong(value_items[i]); + const int v = PyLong_AsLong(value_items[i]); value[i] = v; } return i; @@ -3097,7 +3098,7 @@ static int prop_subscript_ass_array_slice(PointerRNA *ptr, } int dimsize[3]; - int totdim = RNA_property_array_dimension(ptr, prop, dimsize); + const int totdim = RNA_property_array_dimension(ptr, prop, dimsize); if (totdim > 1) { BLI_assert(dimsize[arraydim] == length); } @@ -3247,7 +3248,7 @@ static int pyrna_prop_array_ass_subscript(BPy_PropertyArrayRNA *self, } else if (PyIndex_Check(key)) { - Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); + const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError); if (i == -1 && PyErr_Occurred()) { ret = -1; } @@ -3256,7 +3257,7 @@ static int pyrna_prop_array_ass_subscript(BPy_PropertyArrayRNA *self, } } else if (PySlice_Check(key)) { - Py_ssize_t len = pyrna_prop_array_length(self); + const Py_ssize_t len = pyrna_prop_array_length(self); Py_ssize_t start, stop, step, slicelength; if (PySlice_GetIndicesEx(key, len, &start, &stop, &step, &slicelength) < 0) { @@ -4249,7 +4250,7 @@ static PyObject *pyrna_struct_getattro(BPy_StructRNA *self, PyObject *pyname) ListBase newlb; short newtype; - int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); + const int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); if (done == 1) { /* Found. */ switch (newtype) { @@ -4401,7 +4402,7 @@ static int pyrna_struct_meta_idprop_setattro(PyObject *cls, PyObject *attr, PyOb if (value) { /* Check if the value is a property. */ if (is_deferred_prop) { - int ret = deferred_register_prop(srna, attr, value); + const int ret = deferred_register_prop(srna, attr, value); if (ret == -1) { /* Error set. */ return ret; @@ -4471,7 +4472,7 @@ static int pyrna_struct_setattro(BPy_StructRNA *self, PyObject *pyname, PyObject ListBase newlb; short newtype; - int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); + const int done = CTX_data_get(C, name, &newptr, &newlb, &newtype); if (done == 1) { PyErr_Format( @@ -4646,7 +4647,7 @@ static PyObject *pyrna_prop_collection_idprop_add(BPy_PropertyRNA *self) static PyObject *pyrna_prop_collection_idprop_remove(BPy_PropertyRNA *self, PyObject *value) { - int key = PyLong_AsLong(value); + const int key = PyLong_AsLong(value); #ifdef USE_PEDANTIC_WRITE if (rna_disallow_writes && rna_id_write_error(&self->ptr, NULL)) { @@ -5172,7 +5173,7 @@ static int foreach_parse_args(BPy_PropertyRNA *self, static bool foreach_compat_buffer(RawPropertyType raw_type, int attr_signed, const char *format) { - char f = format ? *format : 'B'; /* B is assumed when not set */ + const char f = format ? *format : 'B'; /* B is assumed when not set */ switch (raw_type) { case PROP_RAW_CHAR: @@ -5400,7 +5401,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self, PyObject *item = NULL; Py_ssize_t i, seq_size, size; void *array = NULL; - PropertyType prop_type = RNA_property_type(self->prop); + const PropertyType prop_type = RNA_property_type(self->prop); /* Get/set both take the same args currently. */ PyObject *seq; @@ -5498,7 +5499,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self, } } else { - char f = buf.format ? buf.format[0] : 0; + const char f = buf.format ? buf.format[0] : 0; if ((prop_type == PROP_INT && (buf.itemsize != sizeof(int) || (f != 'l' && f != 'i'))) || (prop_type == PROP_FLOAT && (buf.itemsize != sizeof(float) || f != 'f'))) { PyBuffer_Release(&buf); @@ -8030,7 +8031,7 @@ static int rna_function_arg_count(FunctionRNA *func, int *min_count) const ListBase *lb = RNA_function_defined_parameters(func); PropertyRNA *parm; Link *link; - int flag = RNA_function_flag(func); + const int flag = RNA_function_flag(func); const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); int count = is_staticmethod ? 0 : 1; bool done_min_count = false; @@ -8273,7 +8274,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param ParameterIterator iter; PointerRNA funcptr; int err = 0, i, ret_len = 0, arg_count; - int flag = RNA_function_flag(func); + const int flag = RNA_function_flag(func); const bool is_staticmethod = (flag & FUNC_NO_SELF) && !(flag & FUNC_USE_SELF_TYPE); const bool is_classmethod = (flag & FUNC_NO_SELF) && (flag & FUNC_USE_SELF_TYPE); @@ -9015,7 +9016,7 @@ void pyrna_struct_type_extend_capi(struct StructRNA *srna, py_method = PyCFunction_New(method, NULL); } - int err = PyDict_SetItemString(dict, method->ml_name, py_method); + const int err = PyDict_SetItemString(dict, method->ml_name, py_method); Py_DECREF(py_method); BLI_assert(!(err < 0)); UNUSED_VARS_NDEBUG(err); diff --git a/source/blender/python/intern/bpy_rna_anim.c b/source/blender/python/intern/bpy_rna_anim.c index ae19f89c348..1d52706c5f9 100644 --- a/source/blender/python/intern/bpy_rna_anim.c +++ b/source/blender/python/intern/bpy_rna_anim.c @@ -131,7 +131,7 @@ static int pyrna_struct_anim_args_parse_ex(PointerRNA *ptr, } } else { - int array_len = RNA_property_array_length(&r_ptr, prop); + const int array_len = RNA_property_array_length(&r_ptr, prop); if ((*r_index) < -1 || (*r_index) >= array_len) { PyErr_Format(PyExc_TypeError, "%.200s index out of range \"%s\", given %d, array length is %d", @@ -316,7 +316,7 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb int index = -1; float cfra = FLT_MAX; const char *group_name = NULL; - char keytype = BEZT_KEYTYPE_KEYFRAME; /* XXX: Expose this as a one-off option... */ + const char keytype = BEZT_KEYTYPE_KEYFRAME; /* XXX: Expose this as a one-off option... */ int options = 0; PYRNA_STRUCT_CHECK_OBJ(self); diff --git a/source/blender/python/intern/bpy_rna_array.c b/source/blender/python/intern/bpy_rna_array.c index 66e07d556a6..cb3fe9cb600 100644 --- a/source/blender/python/intern/bpy_rna_array.c +++ b/source/blender/python/intern/bpy_rna_array.c @@ -361,7 +361,7 @@ static int validate_array(PyObject *rvalue, const char *error_prefix) { int dimsize[MAX_ARRAY_DIMENSION]; - int totdim = RNA_property_array_dimension(ptr, prop, dimsize); + const int totdim = RNA_property_array_dimension(ptr, prop, dimsize); /* validate type first because length validation may modify property array length */ @@ -466,7 +466,7 @@ static char *copy_values(PyObject *seq, const ItemConvert_FuncArg *convert_item, RNA_SetIndexFunc rna_set_index) { - int totdim = RNA_property_array_dimension(ptr, prop, NULL); + const int totdim = RNA_property_array_dimension(ptr, prop, NULL); const Py_ssize_t seq_size = PySequence_Size(seq); Py_ssize_t i; @@ -487,7 +487,7 @@ static char *copy_values(PyObject *seq, if (dim == 0) { if (MatrixObject_Check(seq)) { MatrixObject *pymat = (MatrixObject *)seq; - size_t allocsize = pymat->num_col * pymat->num_row * sizeof(float); + const size_t allocsize = pymat->num_col * pymat->num_row * sizeof(float); /* read callback already done by validate */ /* since this is the first iteration we can assume data is allocated */ @@ -993,7 +993,7 @@ PyObject *pyrna_py_from_array(PointerRNA *ptr, PropertyRNA *prop) /* TODO, multi-dimensional arrays */ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) { - int len = RNA_property_array_length(ptr, prop); + const int len = RNA_property_array_length(ptr, prop); int type; int i; @@ -1011,7 +1011,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) switch (type) { case PROP_FLOAT: { - float value_f = PyFloat_AsDouble(value); + const float value_f = PyFloat_AsDouble(value); if (value_f == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; @@ -1044,7 +1044,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) break; } case PROP_INT: { - int value_i = PyC_Long_AsI32(value); + const int value_i = PyC_Long_AsI32(value); if (value_i == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; @@ -1077,7 +1077,7 @@ int pyrna_array_contains_py(PointerRNA *ptr, PropertyRNA *prop, PyObject *value) break; } case PROP_BOOLEAN: { - int value_i = PyC_Long_AsBool(value); + const int value_i = PyC_Long_AsBool(value); if (value_i == -1 && PyErr_Occurred()) { PyErr_Clear(); return 0; diff --git a/source/blender/python/intern/bpy_rna_callback.c b/source/blender/python/intern/bpy_rna_callback.c index 976b8a65ac7..2f8be0c44e0 100644 --- a/source/blender/python/intern/bpy_rna_callback.c +++ b/source/blender/python/intern/bpy_rna_callback.c @@ -84,7 +84,7 @@ static void cb_region_draw(const bContext *C, ARegion *UNUSED(region), void *cus static PyObject *PyC_Tuple_CopySized(PyObject *src, int len_dst) { PyObject *dst = PyTuple_New(len_dst); - int len_src = PyTuple_GET_SIZE(src); + const int len_src = PyTuple_GET_SIZE(src); BLI_assert(len_src <= len_dst); for (int i = 0; i < len_src; i++) { PyObject *item = PyTuple_GET_ITEM(src, i); diff --git a/source/blender/python/intern/bpy_rna_driver.c b/source/blender/python/intern/bpy_rna_driver.c index 9240e34bbab..3bddd0ad8c0 100644 --- a/source/blender/python/intern/bpy_rna_driver.c +++ b/source/blender/python/intern/bpy_rna_driver.c @@ -57,7 +57,7 @@ PyObject *pyrna_driver_get_variable_value(struct ChannelDriver *driver, struct D } else { /* object & property */ - PropertyType type = RNA_property_type(prop); + const PropertyType type = RNA_property_type(prop); if (type == PROP_ENUM) { /* Note that enum's are converted to strings by default, * we want to avoid that, see: T52213 */ diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index 4ef718ef023..575824e8a86 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -63,7 +63,7 @@ static void py_rna_gizmo_handler_get_cb(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, void *value_p) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data; PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_GET], NULL); @@ -110,7 +110,7 @@ static void py_rna_gizmo_handler_set_cb(const wmGizmo *UNUSED(gz), wmGizmoProperty *gz_prop, const void *value_p) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data; @@ -159,7 +159,7 @@ static void py_rna_gizmo_handler_range_get_cb(const wmGizmo *UNUSED(gz), { struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data; - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); PyObject *ret = PyObject_CallObject(data->fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET], NULL); if (ret == NULL) { @@ -211,7 +211,7 @@ static void py_rna_gizmo_handler_free_cb(const wmGizmo *UNUSED(gz), wmGizmoPrope { struct BPyGizmoHandlerUserData *data = gz_prop->custom_func.user_data; - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); for (int i = 0; i < BPY_GIZMO_FN_SLOT_LEN; i++) { Py_XDECREF(data->fn_slots[i]); } @@ -234,7 +234,7 @@ PyDoc_STRVAR( " :type range: callable\n"); static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { - PyGILState_STATE gilstate = PyGILState_Ensure(); + const PyGILState_STATE gilstate = PyGILState_Ensure(); struct { PyObject *self; @@ -368,7 +368,7 @@ static PyObject *bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *ar return PyC_Tuple_PackArray_F32(value, array_len); } - float value = WM_gizmo_target_property_float_get(gz, gz_prop); + const float value = WM_gizmo_target_property_float_get(gz, gz_prop); return PyFloat_FromDouble(value); break; diff --git a/source/blender/python/mathutils/mathutils.c b/source/blender/python/mathutils/mathutils.c index 308d2ef9618..ca38d7008f6 100644 --- a/source/blender/python/mathutils/mathutils.c +++ b/source/blender/python/mathutils/mathutils.c @@ -208,7 +208,7 @@ int mathutils_array_parse( if (size != -1) { if (flag & MU_ARRAY_ZERO) { - int size_left = array_max - size; + const int size_left = array_max - size; if (size_left) { memset(&array[size], 0, sizeof(float) * size_left); } @@ -541,9 +541,9 @@ int EXPP_FloatsAreEqual(float af, float bf, int maxDiff) { /* solid, fast routine across all platforms * with constant time behavior */ - int ai = *(int *)(&af); - int bi = *(int *)(&bf); - int test = SIGNMASK(ai ^ bi); + const int ai = *(int *)(&af); + const int bi = *(int *)(&bf); + const int test = SIGNMASK(ai ^ bi); int diff, v1, v2; assert((0 == test) || (0xFFFFFFFF == test)); diff --git a/source/blender/python/mathutils/mathutils_Color.c b/source/blender/python/mathutils/mathutils_Color.c index 6bffff467cd..8a7f782de3c 100644 --- a/source/blender/python/mathutils/mathutils_Color.c +++ b/source/blender/python/mathutils/mathutils_Color.c @@ -749,7 +749,7 @@ PyDoc_STRVAR(Color_channel_hsv_v_doc, "HSV Value component in [0, 1].\n\n:type: static PyObject *Color_channel_hsv_get(ColorObject *self, void *type) { float hsv[3]; - int i = POINTER_AS_INT(type); + const int i = POINTER_AS_INT(type); if (BaseMath_ReadCallback(self) == -1) { return NULL; @@ -763,7 +763,7 @@ static PyObject *Color_channel_hsv_get(ColorObject *self, void *type) static int Color_channel_hsv_set(ColorObject *self, PyObject *value, void *type) { float hsv[3]; - int i = POINTER_AS_INT(type); + const int i = POINTER_AS_INT(type); float f = PyFloat_AsDouble(value); if (f == -1 && PyErr_Occurred()) { diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index 236bb1de29d..0a524cbf24c 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -809,7 +809,7 @@ static PyObject *C_Matrix_OrthoProjection(PyObject *cls, PyObject *args) else { /* arbitrary plane */ - int vec_size = (matSize == 2 ? 2 : 3); + const int vec_size = (matSize == 2 ? 2 : 3); float tvec[4]; if (mathutils_array_parse(tvec, @@ -2156,7 +2156,8 @@ static PyObject *Matrix_str(MatrixObject *self) for (col = 0; col < self->num_col; col++) { maxsize[col] = 0; for (row = 0; row < self->num_row; row++) { - int size = BLI_snprintf(dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col)); + const int size = BLI_snprintf( + dummy_buf, sizeof(dummy_buf), "%.4f", MATRIX_ITEM(self, row, col)); maxsize[col] = max_ii(maxsize[col], size); } } diff --git a/source/blender/python/mathutils/mathutils_Vector.c b/source/blender/python/mathutils/mathutils_Vector.c index 3ee6e766413..9bc8c0dffed 100644 --- a/source/blender/python/mathutils/mathutils_Vector.c +++ b/source/blender/python/mathutils/mathutils_Vector.c @@ -356,7 +356,7 @@ PyDoc_STRVAR(Vector_normalize_doc, " however 4D Vectors w axis is left untouched.\n"); static PyObject *Vector_normalize(VectorObject *self) { - int size = (self->size == 4 ? 3 : self->size); + const int size = (self->size == 4 ? 3 : self->size); if (BaseMath_ReadCallback_ForWrite(self) == -1) { return NULL; } @@ -2027,7 +2027,7 @@ static PyObject *Vector_richcmpr(PyObject *objectA, PyObject *objectB, int compa { VectorObject *vecA = NULL, *vecB = NULL; int result = 0; - double epsilon = 0.000001f; + const double epsilon = 0.000001f; double lenA, lenB; if (!VectorObject_Check(objectA) || !VectorObject_Check(objectB)) { diff --git a/source/blender/python/mathutils/mathutils_bvhtree.c b/source/blender/python/mathutils/mathutils_bvhtree.c index 16ea05771d0..1d477421e30 100644 --- a/source/blender/python/mathutils/mathutils_bvhtree.c +++ b/source/blender/python/mathutils/mathutils_bvhtree.c @@ -589,7 +589,7 @@ static PyObject *py_bvhtree_overlap(PyBVHTree *self, PyBVHTree *other) /* pass */ } else { - bool use_unique = (self->orig_index || other->orig_index); + const bool use_unique = (self->orig_index || other->orig_index); GSet *pair_test = use_unique ? BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, overlap_len) : NULL; @@ -1037,7 +1037,7 @@ static Mesh *bvh_get_mesh(const char *funcname, { Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); /* we only need minimum mesh data for topology and vertex locations */ - CustomData_MeshMasks data_masks = CD_MASK_BAREMESH; + const CustomData_MeshMasks data_masks = CD_MASK_BAREMESH; const bool use_render = DEG_get_mode(depsgraph) == DAG_EVAL_RENDER; *r_free_mesh = false; diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 37997e9f912..1a161924f96 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -315,7 +315,7 @@ static PyObject *M_Geometry_intersect_tri_tri_2d(PyObject *UNUSED(self), PyObjec } } - bool ret = isect_tri_tri_v2(UNPACK3(tri_pair[0]), UNPACK3(tri_pair[1])); + const bool ret = isect_tri_tri_v2(UNPACK3(tri_pair[0]), UNPACK3(tri_pair[1])); return PyBool_FromLong(ret); } @@ -492,7 +492,7 @@ static PyObject *M_Geometry_intersect_line_plane(PyObject *UNUSED(self), PyObjec PyObject *py_line_a, *py_line_b, *py_plane_co, *py_plane_no; float line_a[3], line_b[3], plane_co[3], plane_no[3]; float isect[3]; - bool no_flip = false; + const bool no_flip = false; if (!PyArg_ParseTuple(args, "OOOO|O&:intersect_line_plane", @@ -1639,7 +1639,6 @@ static PyObject *M_Geometry_delaunay_2d_cdt(PyObject *UNUSED(self), PyObject *ar in.faces_start_table = in_faces_start_table; in.faces_len_table = in_faces_len_table; in.epsilon = epsilon; - in.skip_input_modify = false; res = BLI_delaunay_2d_cdt_calc(&in, output_type); diff --git a/source/blender/python/mathutils/mathutils_kdtree.c b/source/blender/python/mathutils/mathutils_kdtree.c index c3e66546dae..1de3c23838f 100644 --- a/source/blender/python/mathutils/mathutils_kdtree.c +++ b/source/blender/python/mathutils/mathutils_kdtree.c @@ -191,7 +191,7 @@ static int py_find_nearest_cb(void *user_data, int index, const float co[3], flo if (result) { bool use_node; - int ok = PyC_ParseBool(result, &use_node); + const int ok = PyC_ParseBool(result, &use_node); Py_DECREF(result); if (ok) { return (int)use_node; diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 86c9c64098b..41d20fa994a 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -1857,7 +1857,7 @@ static void update_physics_cache(Render *re, baker.bmain = re->main; baker.scene = scene; baker.view_layer = view_layer; - baker.depsgraph = BKE_scene_get_depsgraph(re->main, scene, view_layer, true); + baker.depsgraph = BKE_scene_ensure_depsgraph(re->main, scene, view_layer); baker.bake = 0; baker.render = 1; baker.anim_init = 1; diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index efe600a846a..48f8c9b6fb3 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -329,7 +329,10 @@ typedef struct wmNotifier { #define ND_RENDER_OPTIONS (4 << 16) #define ND_NODES (5 << 16) #define ND_SEQUENCER (6 << 16) +/* Note: If an object was added, removed, merged/joined, ..., it is not enough to notify with + * this. This affects the layer so also send a layer change notifier (e.g. ND_LAYER_CONTENT)! */ #define ND_OB_ACTIVE (7 << 16) +/* See comment on ND_OB_ACTIVE. */ #define ND_OB_SELECT (8 << 16) #define ND_OB_VISIBLE (9 << 16) #define ND_OB_RENDER (10 << 16) @@ -438,7 +441,10 @@ typedef struct wmNotifier { /* subtype 3d view editing */ #define NS_VIEW3D_GPU (16 << 8) -#define NS_VIEW3D_SHADING (16 << 9) +#define NS_VIEW3D_SHADING (17 << 8) + +/* subtype layer editing */ +#define NS_LAYER_COLLECTION (24 << 8) /* action classification */ #define NOTE_ACTION (0x000000FF) @@ -448,7 +454,8 @@ typedef struct wmNotifier { #define NA_REMOVED 4 #define NA_RENAME 5 #define NA_SELECTED 6 -#define NA_PAINTING 7 +#define NA_ACTIVATED 7 +#define NA_PAINTING 8 /* ************** Gesture Manager data ************** */ diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index cecd324ff28..479768c3536 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -470,10 +470,10 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas } else { if (is_depth) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } else { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } is_depth_prev = is_depth; } @@ -492,7 +492,7 @@ static void gizmos_draw_list(const wmGizmoMap *gzmap, const bContext *C, ListBas } if (is_depth_prev) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } } @@ -534,10 +534,10 @@ static void gizmo_draw_select_3d_loop(const bContext *C, } else { if (is_depth) { - GPU_depth_test(true); + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } else { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } is_depth_prev = is_depth; } @@ -560,7 +560,7 @@ static void gizmo_draw_select_3d_loop(const bContext *C, } if (is_depth_prev) { - GPU_depth_test(false); + GPU_depth_test(GPU_DEPTH_NONE); } if (is_depth_skip_prev) { GPU_depth_mask(true); diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 5d0520bb674..a14ccfa104c 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -113,6 +113,12 @@ IDTypeInfo IDType_ID_WM = { .free_data = window_manager_free_data, .make_local = NULL, .foreach_id = window_manager_foreach_id, + .foreach_cache = NULL, + + .blend_write = NULL, + .blend_read_data = NULL, + .blend_read_lib = NULL, + .blend_read_expand = NULL, }; #define MAX_OP_REGISTERED 32 diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 6f3fbf77987..6c9bba0791c 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -720,8 +720,7 @@ static void wm_draw_window_offscreen(bContext *C, wmWindow *win, bool stereo) wm_draw_region_buffer_create(region, false, false); wm_draw_region_bind(region, 0); - GPU_clear_color(0, 0, 0, 0); - GPU_clear(GPU_COLOR_BIT); + GPU_clear_color(0.0f, 0.0f, 0.0f, 0.0f); ED_region_do_draw(C, region); wm_draw_region_unbind(region); @@ -744,7 +743,6 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) * If it becomes a problem we should clear only when window size changes. */ #if 0 GPU_clear_color(0, 0, 0, 0); - GPU_clear(GPU_COLOR_BIT); #endif /* Blit non-overlapping area regions. */ @@ -910,7 +908,7 @@ static bool wm_draw_update_test_window(Main *bmain, bContext *C, wmWindow *win) const wmWindowManager *wm = CTX_wm_manager(C); Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - struct Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + struct Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); bScreen *screen = WM_window_get_active_screen(win); ARegion *region; bool do_draw = false; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index c363fdcc9d4..6f7c074c704 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -350,7 +350,7 @@ void wm_event_do_depsgraph(bContext *C, bool is_after_open_file) * and for until then we have to accept ambiguities when object is shared * across visible view layers and has overrides on it. */ - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, true); + Depsgraph *depsgraph = BKE_scene_ensure_depsgraph(bmain, scene, view_layer); if (is_after_open_file) { DEG_graph_relations_update(depsgraph); DEG_graph_on_visible_update(bmain, depsgraph, true); @@ -390,6 +390,9 @@ void wm_event_do_refresh_wm_and_depsgraph(bContext *C) static void wm_event_execute_timers(bContext *C) { wmWindowManager *wm = CTX_wm_manager(C); + if (UNLIKELY(wm == NULL)) { + return; + } /* Set the first window as context, so that there is some minimal context. This avoids crashes * when calling code that assumes that there is always a window in the context (which many @@ -402,16 +405,17 @@ static void wm_event_execute_timers(bContext *C) /* called in mainloop */ void wm_event_do_notifiers(bContext *C) { - wmWindowManager *wm = CTX_wm_manager(C); wmNotifier *note, *next; wmWindow *win; + /* Run the timer before assigning 'wm' in the unlikely case a timer loads a file, see T80028. */ + wm_event_execute_timers(C); + + wmWindowManager *wm = CTX_wm_manager(C); if (wm == NULL) { return; } - wm_event_execute_timers(C); - /* disable? - keep for now since its used for window level notifiers. */ #if 1 /* cache & catch WM level notifiers, such as frame change, scene/screen set */ @@ -3189,10 +3193,9 @@ void wm_event_do_handlers(bContext *C) wm_event_free_all(win); } else { - Main *bmain = CTX_data_main(C); Scene *scene = WM_window_get_active_scene(win); ViewLayer *view_layer = WM_window_get_active_view_layer(win); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); Scene *scene_eval = (depsgraph != NULL) ? DEG_get_evaluated_scene(depsgraph) : NULL; if (scene_eval != NULL) { diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 62d9c099cd5..f53a3d6bf35 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -1576,7 +1576,7 @@ void wm_autosave_location(char *filepath) * Blender installed on D:\ drive, D:\ drive has D:\tmp\ * Now, BLI_exists() will find '/tmp/' exists, but * BLI_make_file_string will create string that has it most likely on C:\ - * through get_default_root(). + * 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); diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index 67733ffc673..b245bbe054d 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -353,7 +353,7 @@ static void draw_filled_lasso(wmGesture *gt) draw_filled_lasso_px_cb, &lasso_fill_data); - GPU_blend(GPU_BLEND_ADDITIVE); + GPU_blend(GPU_BLEND_ADDITIVE_PREMULT); IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR); GPU_shader_bind(state.shader); @@ -361,7 +361,7 @@ static void draw_filled_lasso(wmGesture *gt) state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red); immDrawPixelsTex( - &state, rect.xmin, rect.ymin, w, h, GL_R8, false, pixel_buf, 1.0f, 1.0f, NULL); + &state, rect.xmin, rect.ymin, w, h, GPU_R8, false, pixel_buf, 1.0f, 1.0f, NULL); GPU_shader_unbind(); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index e8f663b0c4a..b85bf8cb323 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -210,7 +210,7 @@ static void sound_jack_sync_callback(Main *bmain, int mode, double time) continue; } ViewLayer *view_layer = WM_window_get_active_view_layer(window); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); if (depsgraph == NULL) { continue; } diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index 9d472ed4597..86d3f7f35dc 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -310,8 +310,7 @@ static void playanim_toscreen( CLAMP(offs_x, 0.0f, 1.0f); CLAMP(offs_y, 0.0f, 1.0f); - GPU_clear_color(0.1, 0.1, 0.1, 0.0); - GPU_clear(GPU_COLOR_BIT); + GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f); /* checkerboard for case alpha */ if (ibuf->planes == 32) { @@ -1313,8 +1312,7 @@ static char *wm_main_playanim_intern(int argc, const char **argv) maxwiny = ibuf->y * (1 + (maxwiny / ibuf->y)); } - GPU_clear_color(0.1, 0.1, 0.1, 0.0); - GPU_clear(GPU_COLOR_BIT); + GPU_clear_color(0.1f, 0.1f, 0.1f, 0.0f); int win_x, win_y; playanim_window_get_size(&win_x, &win_y); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index ac00fc36a89..69c08427c4a 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -645,11 +645,10 @@ static void wm_window_ghostwindow_add(wmWindowManager *wm, GHOST_SetWindowState(ghostwin, (GHOST_TWindowState)win->windowstate); } #endif - /* until screens get drawn, make it nice gray */ - GPU_clear_color(0.55, 0.55, 0.55, 1.0f); /* Crash on OSS ATI: bugs.launchpad.net/ubuntu/+source/mesa/+bug/656100 */ if (!GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_UNIX, GPU_DRIVER_OPENSOURCE)) { - GPU_clear(GPU_COLOR_BIT); + /* until screens get drawn, make it nice gray */ + GPU_clear_color(0.55f, 0.55f, 0.55f, 1.0f); } /* needed here, because it's used before it reads userdef */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index 6f96d2ea6a0..5630d294e8d 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -126,7 +126,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) /* In case a framebuffer is still bound from drawing the last eye. */ GPU_framebuffer_restore(); /* Some systems have drawing glitches without this. */ - GPU_clear(GPU_DEPTH_BIT); + GPU_clear_depth(1.0f); /* Draws the view into the surface_data->viewport's framebuffers */ ED_view3d_draw_offscreen_simple(draw_data->depsgraph, diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 78dadbf9513..b9ef40e3398 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -195,7 +195,7 @@ static void wm_xr_session_scene_and_evaluated_depsgraph_get(Main *bmain, Scene *scene = WM_window_get_active_scene(root_win); ViewLayer *view_layer = WM_window_get_active_view_layer(root_win); - Depsgraph *depsgraph = BKE_scene_get_depsgraph(bmain, scene, view_layer, false); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); BLI_assert(scene && view_layer && depsgraph); BKE_scene_graph_evaluated_ensure(depsgraph, bmain); *r_scene = scene; diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index f923c834e93..d00285adb02 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -115,6 +115,10 @@ if(WITH_XR_OPENXR) add_definitions(-DWITH_XR_OPENXR) endif() +if(WITH_GMP) + add_definitions(-DWITH_GMP) +endif() + # Setup the exe sources and buildinfo set(SRC creator.c @@ -191,6 +195,12 @@ if(WITH_BUILDINFO) message(FATAL_ERROR "File \"${buildinfo_h_fake}\" found, this should never be created, remove!") endif() + # From the cmake documentation "If the output of the custom command is not actually created as a + # file on disk it should be marked with the SYMBOLIC source file property." + # + # Not doing this leads to build warnings for the not generated file on windows when using msbuild + 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}) diff --git a/source/creator/creator_args.c b/source/creator/creator_args.c index 164e670c444..0d1c932d2d2 100644 --- a/source/creator/creator_args.c +++ b/source/creator/creator_args.c @@ -751,6 +751,25 @@ static int arg_handle_abort_handler_disable(int UNUSED(argc), return 0; } +static void clog_abort_on_error_callback(void *fp) +{ + BLI_system_backtrace(fp); + fflush(fp); + abort(); +} + +static const char arg_handle_debug_exit_on_error_doc[] = + "\n\t" + "Immediately exit when internal errors are detected."; +static int arg_handle_debug_exit_on_error(int UNUSED(argc), + const char **UNUSED(argv), + void *UNUSED(data)) +{ + MEM_enable_fail_on_memleak(); + CLG_error_fn_set(clog_abort_on_error_callback); + return 0; +} + static const char arg_handle_background_mode_set_doc[] = "\n\t" "Run in background (often used for UI-less rendering)."; @@ -1300,7 +1319,7 @@ static int arg_handle_register_extension(int UNUSED(argc), const char **UNUSED(a if (data) { G.background = 1; } - RegisterBlendExtension(); + BLI_windows_register_blend_extension(G.background); # else (void)data; /* unused */ # endif @@ -2214,6 +2233,7 @@ void main_args_setup(bContext *C, bArgs *ba) "--debug-gpu-force-workarounds", CB_EX(arg_handle_debug_mode_generic_set, gpumem), (void *)G_DEBUG_GPU_FORCE_WORKAROUNDS); + BLI_argsAdd(ba, 1, NULL, "--debug-exit-on-error", CB(arg_handle_debug_exit_on_error), NULL); BLI_argsAdd(ba, 1, NULL, "--verbose", CB(arg_handle_verbosity_set), NULL); diff --git a/source/tools b/source/tools -Subproject ea2a2fa54ecde677aaf4babf54ef4811adc82cc +Subproject f4aa2de034d359bf85c6a0f90c0d045c5858eb8 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0ee3b500fdf..8f72db07d92 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -44,7 +44,7 @@ unset(_default_test_python_exe) # Standard Blender arguments for running tests. # Specify exit code so that if a Python script error happens, the test fails. -set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --python-exit-code 1) +set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --python-exit-code 1) # Python CTests if(WITH_BLENDER AND WITH_PYTHON) diff --git a/tests/gtests/testing/testing_main.cc b/tests/gtests/testing/testing_main.cc index 36d39a1b3b9..6238101b319 100644 --- a/tests/gtests/testing/testing_main.cc +++ b/tests/gtests/testing/testing_main.cc @@ -48,7 +48,9 @@ const std::string &flags_test_release_dir() int main(int argc, char **argv) { + MEM_use_guarded_allocator(); MEM_init_memleak_detection(); + MEM_enable_fail_on_memleak(); testing::InitGoogleTest(&argc, argv); BLENDER_GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, true); google::InitGoogleLogging(argv[0]); diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 18f61d83c3c..617313ee1c3 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -154,13 +154,13 @@ add_blender_test( --run-all-tests ) -add_blender_test( - bmesh_boolean - ${TEST_SRC_DIR}/modeling/bool_regression.blend - --python ${TEST_PYTHON_DIR}/boolean_operator.py - -- - --run-all-tests -) +# add_blender_test( +# bmesh_boolean +# ${TEST_SRC_DIR}/modeling/bool_regression.blend +# --python ${TEST_PYTHON_DIR}/boolean_operator.py +# -- +# --run-all-tests +#) add_blender_test( bmesh_split_faces @@ -176,13 +176,13 @@ add_blender_test( --python-text run_tests.py ) -add_blender_test( - modifiers - ${TEST_SRC_DIR}/modeling/modifiers.blend - --python ${TEST_PYTHON_DIR}/modifiers.py - -- - --run-all-tests -) +#add_blender_test( +# modifiers +# ${TEST_SRC_DIR}/modeling/modifiers.blend +# --python ${TEST_PYTHON_DIR}/modifiers.py +# -- +# --run-all-tests +#) add_blender_test( physics_cloth diff --git a/tests/python/collada/CMakeLists.txt b/tests/python/collada/CMakeLists.txt index bf22de6b762..1b24875578e 100644 --- a/tests/python/collada/CMakeLists.txt +++ b/tests/python/collada/CMakeLists.txt @@ -36,12 +36,12 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR}) # all calls to blender use this if(APPLE) if(${CMAKE_GENERATOR} MATCHES "Xcode") - set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup) + set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error) else() - set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) + set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) endif() else() - set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) + set(TEST_BLENDER_EXE_PARAMS --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) endif() # for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py index bdf4283eb3e..cc949248ce6 100644 --- a/tests/python/cycles_render_tests.py +++ b/tests/python/cycles_render_tests.py @@ -20,6 +20,8 @@ def get_arguments(filepath, output_filepath): "-noaudio", "--factory-startup", "--enable-autoexec", + "--debug-memory", + "--debug-exit-on-error", filepath, "-E", "CYCLES", "-o", output_filepath, diff --git a/tests/python/eevee_render_tests.py b/tests/python/eevee_render_tests.py index a7130136d0a..a90d7730ace 100644 --- a/tests/python/eevee_render_tests.py +++ b/tests/python/eevee_render_tests.py @@ -103,6 +103,8 @@ def get_arguments(filepath, output_filepath): "-noaudio", "--factory-startup", "--enable-autoexec", + "--debug-memory", + "--debug-exit-on-error", filepath, "-E", "BLENDER_EEVEE", "-P", diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index 506c1a1518a..b6cafc2ee24 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -448,16 +448,17 @@ class Report: crash = False output = None try: - output = subprocess.check_output(command) - except subprocess.CalledProcessError as e: - crash = True + completed_process = subprocess.run(command, stdout=subprocess.PIPE) + if completed_process.returncode != 0: + crash = True + output = completed_process.stdout except BaseException as e: crash = True if verbose: print(" ".join(command)) - if output: - print(output.decode("utf-8")) + if (verbose or crash) and output: + print(output.decode("utf-8")) # Detect missing filepaths and consider those errors for filepath, output_filepath in zip(remaining_filepaths[:], output_filepaths): diff --git a/tests/python/modules/test_utils.py b/tests/python/modules/test_utils.py index e31db05ba61..9c23d511813 100755 --- a/tests/python/modules/test_utils.py +++ b/tests/python/modules/test_utils.py @@ -79,6 +79,8 @@ class AbstractBlenderRunnerTest(unittest.TestCase): '-noaudio', '--factory-startup', '--enable-autoexec', + '--debug-memory', + '--debug-exit-on-error', ] if blendfile: diff --git a/tests/python/opengl_draw_tests.py b/tests/python/opengl_draw_tests.py index ab4df63afd9..a2d9d510382 100644 --- a/tests/python/opengl_draw_tests.py +++ b/tests/python/opengl_draw_tests.py @@ -39,6 +39,8 @@ def get_arguments(filepath, output_filepath): "-noaudio", "--factory-startup", "--enable-autoexec", + "--debug-memory", + "--debug-exit-on-error", filepath, "-P", os.path.realpath(__file__), diff --git a/tests/python/view_layer/CMakeLists.txt b/tests/python/view_layer/CMakeLists.txt index 3f38ee4675f..3cac2b6d85f 100644 --- a/tests/python/view_layer/CMakeLists.txt +++ b/tests/python/view_layer/CMakeLists.txt @@ -30,7 +30,7 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${TEST_OUT_DIR}) # endif() # for testing with valgrind prefix: valgrind --track-origins=yes --error-limit=no -set(TEST_BLENDER_EXE $<TARGET_FILE:blender> --background -noaudio --factory-startup --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) +set(TEST_BLENDER_EXE $<TARGET_FILE:blender> --background -noaudio --factory-startup --debug-memory --debug-exit-on-error --env-system-scripts ${CMAKE_SOURCE_DIR}/release/scripts) # ------------------------------------------------------------------------------ diff --git a/tests/python/workbench_render_tests.py b/tests/python/workbench_render_tests.py index 155b54098a8..7c9842d5733 100644 --- a/tests/python/workbench_render_tests.py +++ b/tests/python/workbench_render_tests.py @@ -39,6 +39,8 @@ def get_arguments(filepath, output_filepath): "-noaudio", "--factory-startup", "--enable-autoexec", + "--debug-memory", + "--debug-exit-on-error", filepath, "-E", "BLENDER_WORKBENCH", "-P", |